API REFERENCE

Tickets API

A ticket is the unit of work on the AlgaPSA service desk. Use this API to create tickets, move them through your boards and statuses, assign them to technicians, and log comments, time, and files.

Before you create one

A ticket references a board, a status, a priority, and a client — all by ID. Resolve those first: statuses and priorities have their own lookups (GET /api/v1/tickets/statuses and GET /api/v1/tickets/priorities), while board and client IDs come from their own APIs. The Create a ticket guide walks through the full lookup-then-create flow.

curl
curl https://algapsa.com/api/v1/tickets \
  -H "X-API-Key: $ALGA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "VPN down at the Northwind branch",
    "client_id": "cl_3f9a2c",
    "board_id": "bd_support",
    "status_id": "st_new",
    "priority_id": "pr_high"
  }'

Move it forward

Once a ticket exists, the rest happens through the sub-resources under /tickets/{id}: add notes with POST /comments, change where it sits with PUT /status, hand it off with PUT /assignment, add additional technicians via POST /agents, assign a team with PUT /team, and read logged effort from GET /time-entries. Files, documents, and materials hang off the same ticket.

Manage additional agents and team assignment

Beyond the primary assignee set via PUT /api/v1/tickets/{id}/assignment, you can attach multiple additional technicians and a team to a ticket. These sub-resources were previously only accessible through the Alga PSA UI and are now available via REST.

To add a technician as an additional agent, call POST /api/v1/tickets/{id}/agents with an agent_id in the request body. Retrieve the full list with GET /api/v1/tickets/{id}/agents, and remove one with DELETE /api/v1/tickets/{id}/agents/{userId}. The userId path segment is validated as a UUID — malformed values return a 400 error rather than a server error.

Team assignment works the same way: PUT /api/v1/tickets/{id}/team with a team_id assigns the team, and DELETE /api/v1/tickets/{id}/team removes it. All five routes require ticket:update permission.

If you update the primary assignee on a ticket that already has additional agents, PUT /assignment handles the transition atomically — the internal resource rows are cleared and re-keyed before the assignee field is updated, so the call succeeds even when additional agents are present.

Silent updates

Any ticket mutation that would normally trigger notifications — status changes, reassignments, new comments, or team and agent changes — can skip them for a single operation. Add either or both of these optional fields to the JSON request body:

  • suppressContactNotifications — suppresses customer-facing email and portal notifications.
  • suppressInternalNotifications — additionally suppresses agent, watcher, in-app, and push notifications. Only valid when suppressContactNotifications is also true.

Both flags are accepted by PUT /tickets/{id}, PUT /tickets/{id}/status, PUT /tickets/{id}/assignment, POST /tickets/{id}/comments, POST /tickets/{id}/agents, and PUT /tickets/{id}/team. Silent updates still produce audit records, activity entries, workflow triggers, and webhook deliveries — only the notification emails and pushes are suppressed.

Cc/Bcc external recipients on comments

When posting a public comment with POST /api/v1/tickets/{id}/comments, an MSP agent can Cc or Bcc one-off external email addresses that are not on the ticket's ongoing watch list. AlgaPSA delivers a copy of the comment email to those addresses as real Cc/Bcc headers, then discards them — they do not become ticket watchers and will not receive future notifications.

Supply either or both of these optional arrays in the request body:

  • cc — email addresses copied visibly on this comment.
  • bcc — email addresses copied blind on this comment.

Both arrays are validated: addresses are trimmed and deduplicated, a single address appearing in both lists is promoted to cc, the combined count must not exceed 20 recipients, and one-off recipients are rejected on internal notes. The resolved list is persisted in the comment's email_recipients metadata field and returned on comment read responses. Client-portal contacts can see cc addresses but not bcc addresses on comments they can read.

If the ticket has no requester email or the requester is the comment author, AlgaPSA sends a direct email to the one-off recipients rather than dropping them. A one-off recipient who replies to the thread is recognized on that conversation, but their address is never added to the ticket's permanent watch list.

Find tickets

List and filter with GET /api/v1/tickets. Supported query parameters include status, client, board, priority, assignee, and more. To narrow results to tickets carrying specific tags, pass the tags parameter with a comma-separated list of tag names (e.g. ?tags=vip,urgent); tag values are matched case-insensitively. Run a text query with GET /api/v1/tickets/search, or pull counts for a dashboard from GET /api/v1/tickets/stats. To open a ticket straight from a piece of hardware, use POST /api/v1/tickets/from-asset.

Include tag data

By default, GET /api/v1/tickets omits tag information to keep responses lean. Add fields=tags to the query string to include each ticket's tag array in the response. Each tag entry contains tag_id, tag_text, background_color, and text_color. The parameter works alongside any other filter — for example, GET /api/v1/tickets?fields=tags&status_id=st_open returns all open tickets with their tags attached.

Sort ticket results

GET /api/v1/tickets accepts a sort query parameter to control result order. The value is validated against an explicit allowlist; passing an unrecognised sort key returns 400 Bad Request with a machine-readable error rather than a database error.

Pass sort=latest_activity_at (combined with order=asc or order=desc) to sort tickets by their most recent visible activity — the newest of the ticket's own timestamps and its latest published, non-deleted comment. This surfaces tickets that have had recent conversation even when the underlying ticket record has not otherwise changed, and it matches the ordering the “Last Activity” column in the UI uses, so API results and the list view stay in sync when you apply the same filters.

created_at is accepted as a sort alias for the underlying entered_at column. To include the latest_activity_at timestamp in each response object, add it to the fields parameter alongside any other fields you need. For client-portal callers the value is computed from non-internal comments only, so internal notes do not expose their timing to portal contacts.

Bundle related tickets

When several tickets describe the same underlying issue, group them under one master ticket. Create the bundle with POST /api/v1/tickets/{id}/bundle, choosing a sync mode of link_only to just link the children or sync_updates to propagate the master's changes to them. Adjust membership later with POST /api/v1/tickets/{id}/bundle/children and DELETE /api/v1/tickets/{id}/bundle/children/{childId}, hand the master role to another ticket with POST /api/v1/tickets/{id}/bundle/promote, and change the sync mode through PUT /api/v1/tickets/{id}/bundle/settings. Read a ticket's role (master, child, or standalone) and its full membership from GET /api/v1/tickets/{id}/bundle, or break the whole group apart with DELETE /api/v1/tickets/{id}/bundle.

Linked assets

Service tickets can be linked to the hardware they affect. Use GET /api/v1/tickets/{id}/assets to list all assets currently associated with a ticket; each row includes full asset details and the relationship type describing why it is linked. Create a link with POST /api/v1/tickets/{id}/assets (supply asset_id; optionally pass relationship_type, default affected, and notes), and remove it with DELETE /api/v1/tickets/{id}/assets/{assetId}. Reading linked assets requires ticket:read and asset:read; creating or removing a link also requires ticket:update. The same associations are readable and writable from the asset side — see Assets API.

Delete a ticket

DELETE /api/v1/tickets/{id} permanently removes a ticket and all its owned child records — comments, attachments, SLA notification history, email reply tokens, and project links are cleaned up automatically. If the ticket has records that cannot be removed automatically, such as logged time entries or schedule entries, the call returns 409 Conflict with a details.dependencies array that lists each blocking item so you know exactly what to remove or reassign first. Once the blocking records are cleared, retry the DELETE and it will succeed. A successful delete returns 204 No Content and the ticket is removed from the search index.

Endpoints

Every endpoint links through to its full parameters, request and response schemas, and code samples.

GET/api/v1/ticketsAlgaPSAAlgaDeskList ticketsPOST/api/v1/ticketsAlgaPSAAlgaDeskCreate ticketGET/api/v1/tickets/{id}AlgaPSAAlgaDeskGet ticketPUT/api/v1/tickets/{id}AlgaPSAAlgaDeskUpdate ticketDELETE/api/v1/tickets/{id}AlgaPSAAlgaDeskDelete ticketGET/api/v1/tickets/{id}/agentsAlgaPSAAlgaDeskList ticket agentsPOST/api/v1/tickets/{id}/agentsAlgaPSAAlgaDeskAdd a ticket additional agentDELETE/api/v1/tickets/{id}/agents/{userId}AlgaPSAAlgaDeskRemove a ticket additional agentGET/api/v1/tickets/{id}/assetsAlgaPSAList assets linked to a ticketPOST/api/v1/tickets/{id}/assetsAlgaPSALink an asset to a ticketDELETE/api/v1/tickets/{id}/assets/{assetId}AlgaPSAUnlink an asset from a ticketPUT/api/v1/tickets/{id}/assignmentAlgaPSAAlgaDeskUpdate ticket assignmentGET/api/v1/tickets/{id}/bundleAlgaPSAAlgaDeskGet ticket bundlePOST/api/v1/tickets/{id}/bundleAlgaPSAAlgaDeskCreate ticket bundleDELETE/api/v1/tickets/{id}/bundleAlgaPSAAlgaDeskUnbundle ticketPOST/api/v1/tickets/{id}/bundle/childrenAlgaPSAAlgaDeskAdd bundle childrenDELETE/api/v1/tickets/{id}/bundle/children/{childId}AlgaPSAAlgaDeskRemove bundle childPOST/api/v1/tickets/{id}/bundle/promoteAlgaPSAAlgaDeskPromote bundle masterPUT/api/v1/tickets/{id}/bundle/settingsAlgaPSAAlgaDeskUpdate ticket bundle settingsGET/api/v1/tickets/{id}/checklistAlgaPSAAlgaDeskList ticket checklist itemsPOST/api/v1/tickets/{id}/checklistAlgaPSAAlgaDeskAdd a ticket checklist itemPATCH/api/v1/tickets/{id}/checklist/{itemId}AlgaPSAAlgaDeskSet ticket checklist item completionGET/api/v1/tickets/{id}/commentsAlgaPSAAlgaDeskList ticket commentsPOST/api/v1/tickets/{id}/commentsAlgaPSAAlgaDeskAdd ticket commentPUT/api/v1/tickets/{id}/comments/{commentId}AlgaPSAAlgaDeskUpdate a ticket commentPOST/api/v1/tickets/{id}/comments/{commentId}/reactionsAlgaPSAAlgaDeskToggle a comment reactionDELETE/api/v1/tickets/{id}/comments/{commentId}/scheduleAlgaPSAAlgaDeskCancel a scheduled commentGET/api/v1/tickets/{id}/documentsAlgaPSAAlgaDeskList ticket documentsPOST/api/v1/tickets/{id}/documentsAlgaPSAAlgaDeskUpload a ticket documentGET/api/v1/tickets/{id}/documents/{documentId}AlgaPSAAlgaDeskDownload a ticket documentDELETE/api/v1/tickets/{id}/documents/{documentId}AlgaPSAAlgaDeskDelete a ticket documentGET/api/v1/tickets/{id}/documents/{documentId}/previewAlgaPSAAlgaDeskGet a ticket document preview imageGET/api/v1/tickets/{id}/documents/{documentId}/thumbnailAlgaPSAAlgaDeskGet a ticket document thumbnail imageGET/api/v1/tickets/{id}/external-linksAlgaPSAAlgaDeskList ticket external linksPOST/api/v1/tickets/{id}/external-linksAlgaPSAAlgaDeskAdd a ticket external linkPATCH/api/v1/tickets/{id}/external-links/{linkId}AlgaPSAAlgaDeskUpdate a ticket external linkDELETE/api/v1/tickets/{id}/external-links/{linkId}AlgaPSAAlgaDeskRemove a ticket external linkGET/api/v1/tickets/{id}/materialsAlgaPSAList ticket materialsPOST/api/v1/tickets/{id}/materialsAlgaPSAAdd a ticket materialPUT/api/v1/tickets/{id}/statusAlgaPSAAlgaDeskUpdate ticket statusPUT/api/v1/tickets/{id}/teamAlgaPSAAlgaDeskAssign a team to a ticketDELETE/api/v1/tickets/{id}/teamAlgaPSAAlgaDeskRemove a ticket team assignmentGET/api/v1/tickets/{id}/time-entriesAlgaPSAList ticket time entriesGET/api/v1/tickets/by-external-linkAlgaPSAAlgaDeskFind a ticket by external linkGET/api/v1/tickets/from-assetAlgaPSACreate ticket from asset (POST only)POST/api/v1/tickets/from-assetAlgaPSACreate ticket from assetGET/api/v1/tickets/prioritiesAlgaPSAAlgaDeskList ticket prioritiesGET/api/v1/tickets/searchAlgaPSAAlgaDeskSearch ticketsGET/api/v1/tickets/statsAlgaPSAAlgaDeskGet ticket statsGET/api/v1/tickets/statusesAlgaPSAAlgaDeskList ticket statuses