AutoTask PSA Setup
AVA's core value is its native Datto AutoTask PSA integration: every chat
becomes a real AutoTask ticket, every close writes notes, attachments, and a
billed time entry. This page is the complete, ordered guide to wiring AVA to
your AutoTask instance — from creating the API user to the per-instance
picklist gotchas that trip up most first-time setups.
Work through it top to bottom. Each step names the environment variable(s) it
produces; the full variable reference is in
configuration.md, and the overall bring-up order is in
quickstart.md.
What AVA does in AutoTask
Across one chat's lifecycle, AVA:
- At chat start — matches the client's email domain to a Company, finds or
creates the Contact, and creates a Ticket with your configured default
status/priority/queue/source/type, plus the AI-triaged priority (P1–P4
mapped to your priority picklist). - During the chat — nothing is written; the conversation lives in AVA.
- At close (wrap-up) — updates the ticket status and resolution, posts the
engineer's client-facing and internal notes plus a full "Chat Transcript"
note, uploads any files shared during the chat, and creates a time entry
attributed to the engineer's AutoTask Resource.
Everything is server-side. AutoTask credentials never reach the browser.
Step 1 — Create an API-only user
In AutoTask: Admin → Resources/Users → New → API User. Do not reuse a
human login — API users carry their own credential and security level, and a
human password change would silently break the integration.
Collect four values:
| Value | Maps to | Notes |
|---|---|---|
| API user email/username | AUTOTASK_API_USER | e.g. [email protected] |
| API user secret/password | AUTOTASK_API_SECRET | AutoTask generates this. It often contains a $ — AVA reads it raw from the env file to avoid variable-expansion mangling, and you must wrap it in double quotes in the env file: AUTOTASK_API_SECRET="ab$cd$ef" |
| API tracking integration code | AUTOTASK_API_INTEGRATION_CODE | The integration code AutoTask requires alongside the API user |
| Zone (data-center) REST URL | AUTOTASK_API_ZONE_URL | Discovered in Step 3 |
Step 2 — Security level: the entity grants AVA needs
The API user's security level must grant access to exactly the entities AVA
touches. AVA exercises these eight:
| AutoTask entity | Operations AVA performs |
|---|---|
| Tickets | Create (new chat), query by id, update (resolution + status on close), read field metadata (picklist discovery) |
| Ticket Notes (child of Tickets) | Create client-facing and internal notes, plus the transcript note |
| Ticket Attachments (child of Tickets) | Upload files shared during the chat |
| Time Entries | Create the billed time entry on close |
| Companies | Query by web address and by name (email-domain → company matching) |
| Contacts | Query by email; create a contact when none exists |
| Resources | Query by email (map each engineer's O365 login to an AutoTask resource) |
| Billing Codes (picklist) | Query to resolve a valid billing code / work type for time entries |
Grant the Web Services API permission and the ability to create/edit
tickets, notes, time entries, attachments, and contacts, and to read companies
and resources. Starting from a stock "API User (system)" security level usually
covers all of this; if your AutoTask admin runs locked-down security levels,
verify each entity explicitly.
Diagnostic rule of thumb: an overly restrictive security level surfaces as
a 403 on one specific entity while everything else works. If ticket creation
succeeds but the close flow fails, check the Time Entries / Ticket Notes
grants first.
⚠️ Account-lockout caution: repeated
401s from a mistyped or mangled
secret can lock the AutoTask API user account. If you hit persistent
401s, check whether the account is locked in AutoTask admin before
debugging configuration — unlocking it (and fixing the quoted secret) is
often the whole fix.
Step 3 — Discover your zone URL
AutoTask assigns each account to a data-center "zone", and your REST base URL
depends on it. Discover yours by querying the (unauthenticated)
zone-information endpoint with your API user's email:
curl "https://webservices.autotask.net/atservicesrest/v1.0/[email protected]"The response's url field is your zone base (e.g.
https://webservices5.autotask.net/ATServicesRest/V1.0). Put it in
AUTOTASK_API_ZONE_URL. AVA normalizes the value — a trailing slash is fine,
and /V1.0 is appended if you omit it — so either of these works:
AUTOTASK_API_ZONE_URL=https://webservices5.autotask.net/ATServicesRest/V1.0
AUTOTASK_API_ZONE_URL=https://webservices5.autotask.net/ATServicesRestStep 4 — Verify connectivity
With the four credential variables set in your env file:
npx tsx scripts/test-autotask.ts
# or, also exercising the domain→company and email→contact matchers:
npx tsx scripts/test-autotask.ts acme-client.com [email protected]A PASS on Test 1 confirms the credential, integration code, and zone URL are
all correct. Failure decoder:
| Symptom | Meaning |
|---|---|
401 | The user / secret / integration-code triple is wrong — or the API user is locked (see the caution in Step 2) |
403 | Credentials are fine; the security level is missing a grant on that specific entity |
| Timeout / connection error | Wrong zone URL, or network egress blocked to *.autotask.net |
Step 5 — Discover your picklist IDs
AutoTask picklist IDs (status, priority, queue, source, ticket type, issue
type…) are specific to your AutoTask instance. Never copy IDs from another
instance, an example, or a screenshot — discover your own:
npx tsx scripts/discover-picklists.tsThis prints your instance's values for status, priority, queueID,
source, ticketType, ticketCategory, issueType, and subIssueType,
marking each [DEFAULT] / [INACTIVE] / [SYSTEM]. Map them into your env
file:
AUTOTASK_DEFAULT_TICKET_STATUS=1 # your "New" status id
AUTOTASK_DEFAULT_TICKET_PRIORITY=3 # your "Normal" priority id
AUTOTASK_DEFAULT_TICKET_QUEUE_ID= # your helpdesk queue id
AUTOTASK_DEFAULT_TICKET_SOURCE= # your "Chat" source id (create one in AT if needed)
AUTOTASK_DEFAULT_TICKET_TYPE= # ticket type for chat-originated ticketsUnder the hood these come from each entity's entityInformation/fields
endpoint — the same place to look if you ever need a picklist the script
doesn't cover.
Step 6 — Map triage priority (P1–P4) to your priority picklist
AVA's AI triage classifies every incoming chat as P1 (critical) through
P4 (low), and files the AutoTask ticket at the corresponding priority:
AUTOTASK_PRIORITY_P1= # Critical — system down, security breach
AUTOTASK_PRIORITY_P2= # High — major feature broken, many users affected
AUTOTASK_PRIORITY_P3= # Normal — single user issue, workaround exists
AUTOTASK_PRIORITY_P4= # Low — question, enhancement, cosmetic⚠️ These four are not a graduated default — set all of them. Each
Pn
resolves independently: if one is unset it falls through to the single shared
AUTOTASK_DEFAULT_TICKET_PRIORITY, and if that is also unset, to a hardcoded
3. Leave them blank and every AI-triaged chat files at the same
AutoTask priority — a P1 outage and a P4 question become indistinguishable in
your queue. Discover your priority picklist IDs in Step 5 and set all four
deliberately. And remember: your instance's priority IDs are almost certainly
not1,2,3,4— never assume.
Step 7 — The note publish value (per-instance gotcha)
publish value (per-instance gotcha)When the engineer closes a chat, AVA posts the client-facing note with a
publish picklist value:
AUTOTASK_NOTE_PUBLISH_EXTERNAL=1 # default: 1 = "Internal Only"The publish field is a per-instance picklist — AutoTask instances define
different valid values, and posting a value your instance doesn't define makes
the note POST fail with a misleading 500 (not a clean 400). The default
1 ("Internal Only") is universally valid; only change it to a value your
instance actually defines (e.g. an "Internal & External" value) after
confirming it exists in your instance's TicketNote field metadata.
Step 8 — Resource role ID for time entries
AUTOTASK_DEFAULT_ROLE_ID= # your instance's resource role idThis role ID is used when assigning the ticket to the accepting engineer and
when posting the billed time entry at close. Its resolution is deliberately
warn-not-fail: if unset — or set to anything that isn't a positive integer
— AVA logs a one-time warning and falls back to a built-in role ID that is
only correct for the reference instance.
That means a missing value boots fine but bills against the wrong role.
Find your role IDs in AutoTask admin (Resources/Roles) and set this
explicitly for your instance.
Step 9 — Catchall company for unknown email domains
AUTOTASK_CATCHALL_COMPANY_ID= # numeric AT company idWhen a client chats in from an email domain not tied to any AutoTask company
(personal Gmail, a prospect, etc.), AVA files the ticket under the catchall
company you designate. To find a company's numeric ID:
npx tsx scripts/test-autotask.ts acme-client.com
# prints: - <Company Name> (ID: 1234, web: ...)Strongly recommended. If left unset, unknown-domain chats start without
an AutoTask ticket and enter a degraded recovery path: the wrap-up flow retries
full ticket creation at close time, and if that also fails the engineer is
offered an explicit, confirmed "close without AutoTask sync" escape hatch (the
transcript stays in AVA either way). Setting a catchall avoids that whole path.
Two notes:
0is a valid AutoTask company ID. AVA distinguishes "unset" from an
explicit0— settingAUTOTASK_CATCHALL_COMPANY_ID=0genuinely enables the
catchall against company 0.- The client chat form shows a soft nudge toward work email addresses, and the
engineer dashboard badges ticketless sessions, so the degraded path is
visible rather than silent — but configuring the catchall is still the right
fix.
How AVA handles AutoTask errors (behavior you can rely on)
- 429 (rate limit) and transient 5xx responses are retried automatically
with exponential backoff (up to 3 retries), and the AutoTask response body is
logged on each attempt so your container logs surface the underlying
complaint. - Mislabeled 5xx errors fail fast. Some AutoTask errors that are really
schema/validation complaints (e.g. a nonexistent picklist value) come back
with a 5xx status. AVA recognizes these and does not burn retries on a
request that can never succeed — the error is logged once with the AT
message. - Non-idempotent POSTs are never retried after a client-side timeout. If a
ticket/note/time-entry POST times out, AutoTask may have already received
it; retrying could create duplicates, so AVA surfaces the error instead.
GET and PATCH (which are safe to repeat) do retry on timeout. - Note length is capped to AutoTask's TicketNote description field limit
before posting, so long transcripts can't trip the field-limit 500.
Environment variable summary
| Variable | Required | Where it came from |
|---|---|---|
AUTOTASK_API_USER | Required to boot | Step 1 |
AUTOTASK_API_SECRET | Required to boot (quote it) | Step 1 |
AUTOTASK_API_INTEGRATION_CODE | Required to boot | Step 1 |
AUTOTASK_API_ZONE_URL | Required to boot | Step 3 |
AUTOTASK_DEFAULT_TICKET_STATUS | Recommended | Step 5 |
AUTOTASK_DEFAULT_TICKET_PRIORITY | Recommended | Step 5 |
AUTOTASK_DEFAULT_TICKET_QUEUE_ID | Recommended | Step 5 |
AUTOTASK_DEFAULT_TICKET_SOURCE | Recommended | Step 5 |
AUTOTASK_DEFAULT_TICKET_TYPE | Recommended | Step 5 |
AUTOTASK_PRIORITY_P1 … P4 | Recommended — set all four | Step 6 |
AUTOTASK_NOTE_PUBLISH_EXTERNAL | Recommended (default 1) | Step 7 |
AUTOTASK_DEFAULT_ROLE_ID | Set it (warn-not-fail) | Step 8 |
AUTOTASK_CATCHALL_COMPANY_ID | Strongly recommended | Step 9 |
AUTOTASK_TIMEOUT_MS | Optional (default 15000) | Per-request REST timeout |
See configuration.md for types, defaults, and the rest
of the configuration surface.
Next steps
entra-sso.md— engineer sign-in via Microsoft Entra ID,
including how engineers map to the AutoTask Resources you granted read access
to above.ninjaone.md— the optional read-only device-at-a-glance
integration.quickstart.md— the end-to-end bring-up, including the
smoke test that proves a chat produces a real ticket.
Updated about 1 hour ago