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 — the conversation itself lives in AVA. Since v1.8.0 an engineer can also, from the console: post internal notes to the ticket (Internal Only, live), and change the ticket's status or priority after an explicit confirmation. Each of those is a normal AutoTask ticket write, so your workflow rules apply to it (see "Workflow rules and the engineer console" below).
- 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 and by contact/company (recent tickets, possible duplicates), update (resolution + status on close; status/priority from the engineer console), read field metadata (picklist discovery) |
| Ticket Notes (child of Tickets) | Create client-facing and internal notes, live engineer internal notes, plus the transcript note; read field metadata (the publish picklist) |
| 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 persistent401s, 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/ATServicesRestThe same response has a webUrl field — the origin of your AutoTask web UI (e.g. https://ww5.autotask.net). The engineer console uses it to turn ticket numbers into links. AVA derives it from a webservicesN zone URL automatically; if your zone URL doesn't follow that pattern, or the links open the wrong host, copy webUrl into:
AUTOTASK_WEB_URL=https://ww5.autotask.net # https only; just the originWithout a usable web URL, ticket numbers simply render as plain text.
Step 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
Pnresolves independently: if one is unset it falls through to the single sharedAUTOTASK_DEFAULT_TICKET_PRIORITY, and if that is also unset, to a hardcoded3. 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.
Engineer priority editing needs a complete mapping. The engineer console lets engineers change a ticket's priority (ticket panel or /priority). It is enabled only when all four AUTOTASK_PRIORITY_P1..P4 are set and distinct; with any missing or duplicated, Priority stays read-only and AVA refuses priority changes rather than guessing.
Status editing needs no mapping — the console reads your live status picklist. It never offers Complete mid-chat (the wrap-up flow completes the ticket). Complete is recognised by its label and by AutoTask's system value 5; if your instance's Complete status uses a different ID, set:
AUTOTASK_COMPLETE_STATUS_ID=5 # your "Complete" status id (default 5)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.
Internal notes: TicketNote.publish
TicketNote.publishEvery engineer-only note AVA posts — live internal notes from the console, the close-time "Chat Transcript", the "Chat internal notes (not synced live)" note and the wrap-up internal note — uses a separate value:
AUTOTASK_NOTE_PUBLISH_INTERNAL=1 # default: 1 = "Internal Only"Verify that this value is Internal Only on your instance before engineers start writing notes — if it mapped to a client-visible option, your team's internal notes would be published to the contact. Read the TicketNote field metadata with your API user (substitute your zone URL and credentials; keep the secret out of your shell history):
curl -s "https://webservicesN.autotask.net/ATServicesRest/V1.0/TicketNotes/entityInformation/fields" \
-H "ApiIntegrationcode: <integration code>" -H "UserName: <api user>" -H "Secret: <secret>"Find the field named publish and check that the picklistValues entry whose value matches AUTOTASK_NOTE_PUBLISH_INTERNAL is labelled "Internal Only" (or your instance's internal-only equivalent) and is active.
Workflow rules and the engineer console
AVA's ticket writes are ordinary AutoTask updates, so your AutoTask workflow rules fire on them. Before go-live, ask your AutoTask admin to check (Admin → Workflow Rules) whether any rule emails the contact when:
- a ticket's status or priority changes — for example a rule on "Waiting Customer". Engineers can change status and priority mid-chat; the console's confirm step already warns "AutoTask workflow rules may notify the contact". If a rule does email clients, decide whether that is acceptable, or agree with your engineers not to pick those statuses from the console;
- a note is added — Internal Only notes don't normally notify contacts, but a rule can be written to. If one does, set
AUTOTASK_SYNC_INTERNAL_NOTES=false: notes then stay in AVA during the chat and reach AutoTask only at close, inside the transcript.
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 will not match your 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_COMPLETE_STATUS_ID | Optional (default 5) | Step 6 — only if your Complete status isn't 5 |
AUTOTASK_NOTE_PUBLISH_EXTERNAL | Recommended (default 1) | Step 7 |
AUTOTASK_NOTE_PUBLISH_INTERNAL | Recommended (default 1) — verify it is Internal Only | Step 7 |
AUTOTASK_SYNC_INTERNAL_NOTES | Optional (default on) | Step 7 — workflow rules |
AUTOTASK_WEB_URL | Optional (derived from the zone URL) | Step 3 |
TICKET_WRITE_RATE_LIMIT_PER_MIN | Optional (default 10) | Console status/priority writes per engineer per minute |
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.engineer-console.md— how engineers use internal notes and the in-chat status/priority editing configured above.
Updated 15 days ago