backupchecks/docs/autotask_rest_api.md
Ivo Oskamp a9a60e3315 Restore Autotask PSA integration
Restored from branch v20260203-13-autotask-resolution-item-wrapper with
the following functionality:

- Autotask REST API client with support for tickets, notes, companies,
  and reference data (queues, sources, priorities, statuses)
- Safe ticket updates preserving stabilizing fields per API contract
- Database models for Autotask settings, customer mapping, and ticket linkage
- Settings UI for Autotask configuration with connection test
- Run Checks integration for ticket creation
- Customers page with Autotask company mapping
- Ticket linking during mail import

Also preserved the require_daily_dashboard_visit setting from the
current branch.

Added docs/autotask_rest_api.md with validated API contract from Postman tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:43:44 +01:00

326 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Autotask REST API Postman Test Contract
## Reference Sources
Primary external reference used during testing:
- https://github.com/AutotaskDevelopment/REST-Postman/blob/main/2020.10.29%20-%20Autotask%20Collecction%20-%20Must%20fill%20in%20variables%20on%20collection.postman_collection.json
This document combines:
- Empirically validated Postman test results
- Official Autotask Postman collection references (where applicable)
---
## Purpose
This document captures **validated Autotask REST API behaviour** based on hands-on Postman testing.
This is an **authoritative test contract**:
- General-purpose
- Product-agnostic
- Based on proven results, not assumptions
If implementation deviates from this document, **the document is correct and the code is wrong**.
---
## 0. Base URLs
### Sandbox
https://webservices19.autotask.net/ATServicesRest/V1.0
### Production
https://webservices19.autotask.net/ATServicesRest/V1.0
Notes:
- `ATServicesRest` is case-sensitive
- Other casing variants are invalid
---
## 0.1 Global Invariants (Do Not Violate)
- TicketID (numeric) is the only authoritative identifier for single-ticket operations
- TicketNumber is display-only and must be resolved to TicketID first
- PATCH is not supported for Tickets
- PUT /Tickets is always a **full update**, never partial
- Never guess fields or values
---
## 1. Ticket Lookup
### Resolve TicketNumber → TicketID
Endpoint:
POST /Tickets/query
Filter:
- field: ticketNumber
- op: eq
- value: <TicketNumber>
Result:
- items[0].id → TicketID
---
## 2. Authoritative Ticket Retrieval
Endpoint:
GET /Tickets/{TicketID}
Always required before any update.
### 2.1 Response Envelope (Critical)
Validated response shape for single-ticket retrieval:
- The ticket object is returned under the `item` wrapper
Example shape:
- `issueType` is at `item.issueType`
- `subIssueType` is at `item.subIssueType`
- `source` is at `item.source`
Implementation rule:
- Always read stabilising fields from `item.*` in the GET response.
- Do **not** read these fields from the response root.
Note:
- PUT payloads are **not** wrapped in `item`. They use the plain ticket object fields at the request body root.
Commonly required stabilising fields:
- id
- ticketNumber
- companyID
- queueID
- title
- priority
- status
- dueDateTime
- ticketCategory
- issueType
- subIssueType
- source
- organizationalLevelAssociationID
- completedDate
- resolvedDateTime
- lastTrackedModificationDateTime
---
## 3. Ticket Status Picklist (ID → Label)
Endpoint:
GET /Tickets/entityInformation/fields
Validated behaviour:
- `status` is an integer picklist
- Picklist values (ID → label) are returned inline
- Status IDs and semantics are tenant-dependent
Do not assume lifecycle meaning based on label alone.
---
## 4. Ticket Update Behaviour
### PATCH
- Not supported
- Returns error indicating unsupported HTTP method
### PUT /Tickets
Validated behaviour:
- Full update required
- Missing fields cause hard failures
- Partial updates are rejected
Implementation rule:
- Always copy required fields from a fresh GET
- Change only the intended field(s)
---
## 5. Status Semantics (Validated Example)
Observed in test tenant:
- Status = 8 (label: "Completed")
- Status updates
- completedDate = null
- resolvedDateTime = null
- Status = 5 (label: "Complete")
- Status updates
- completedDate populated
- resolvedDateTime populated
Conclusion:
- Resolution timestamps depend on status ID, not label
- Validate per tenant before relying on timestamps
---
## 6. Time Entry Existence Check (Decisive)
Entity:
TimeEntries
Endpoint:
POST /TimeEntries/query
Filter:
- field: ticketID
- op: eq
- value: <TicketID>
Decision:
- count = 0 → no time entries
- count > 0 → time entries exist
---
## 7. Ticket Notes via REST Capability vs Endpoint Reality
Although entityInformation reports TicketNote as creatable, **entity-level create does not work**.
### Non-working endpoints
- POST /TicketNotes → 404
- POST /TicketNote → 404
### Working endpoint (only supported method)
POST /Tickets/{TicketID}/Notes
Required fields:
- Title
- Description
- NoteType
- Publish
Result:
- Note is created and immediately visible
Query endpoint works:
- POST /TicketNotes/query
---
## 8. Resolution Field Update (Validated Test)
### Test scope
This section documents the **explicit Postman tests** performed to validate how the `resolution` field can be updated safely.
### Field characteristics (validated)
- name: resolution
- dataType: string
- max length: 32000
- isReadOnly: false
### Critical constraint (proven)
Because **PATCH is not supported** and **PUT /Tickets is a full update**, the resolution field **cannot** be updated in isolation.
Sending an incomplete payload results in unintended changes to:
- classification
- routing
- status
- organizational structure
### Validated update pattern
1. Retrieve current ticket state
- GET /Tickets/{TicketID}
- Read fields from `item.*` (see section 2.1)
2. Construct PUT payload by copying current values of stabilising fields
(explicitly validated in tests):
- id
- companyID
- queueID
- title
- priority
- status
- dueDateTime
- ticketCategory
- issueType
- subIssueType
- source
- organizationalLevelAssociationID
3. Change **only** the `resolution` field
4. Execute update
- PUT /Tickets
### Test result
- Resolution text becomes visible in the Autotask UI
- No unintended changes occur
This behaviour was reproduced consistently and is considered authoritative.
---
## 9. Ticket Resolution Workflow (Validated Tests)
This section captures the **end-to-end resolution tests** performed via Postman.
### Test 1 Resolution without status change
Steps:
1. GET /Tickets/{TicketID}
2. POST /Tickets/{TicketID}/Notes
3. PUT /Tickets (update `resolution` only)
Result:
- Resolution text is updated
- Ticket status remains unchanged
- completedDate and resolvedDateTime remain null
Conclusion:
- Resolution text alone does **not** resolve a ticket
---
### Test 2 Conditional resolution based on time entries
Steps:
1. GET /Tickets/{TicketID}
2. POST /Tickets/{TicketID}/Notes
3. PUT /Tickets (update `resolution`)
4. POST /TimeEntries/query (filter by ticketID)
Decision logic (validated):
- If **no time entries exist**:
- PUT /Tickets with status = 5
- completedDate is set
- resolvedDateTime is set
- If **time entries exist**:
- Status is NOT changed
- Ticket remains open in Autotask
### Key conclusions
- Notes, resolution, and status are independent operations
- Status 5 is the only validated status that sets resolution timestamps
- Status changes must always be explicit and conditional
---
## Non-Negotiable Implementation Rules
- Always GET before PUT
- Never guess stabilising fields
- Never use PATCH
- Never change status implicitly
- Notes and resolution must be explicit
---
## 10. API Contract Summary
Hard rules for code:
- Always resolve TicketNumber → TicketID via POST /Tickets/query
- Always GET /Tickets/{TicketID} before any update
- Never attempt PATCH for Ticket updates
- Use PUT /Tickets with a full, stabilised payload
- Validate per tenant which status values set completedDate/resolvedDateTime
- Check time entries via POST /TimeEntries/query when status decisions depend on them
- Create ticket notes only via /Tickets/{TicketID}/Notes
If code deviates from this document, **the document is correct and the code is wrong**.