Support API, events and bulk export
Everything an integration needs from the support module: the endpoints for cases, threads, SLA, queues, catalog, entitlements and surveys; event subscriptions signed with retry and replay; a feed you resume by cursor; bulk export of cases and messages; and idempotent import of historical cases from another help desk. Every endpoint has a matching tool on the MCP server.
Support has always been reachable by the generic record API: a case is a record, so listing and creating one already worked. What did not work is everything that lives next to the case and is not a record. The message thread, the SLA clock, the queues, the service catalog, the entitlement balance and the surveys had no endpoint at all, so an integration could open a case and could not reply to it.
This article covers the support endpoints, the same tools on the MCP server, the event subscriptions with retry and replay, the resumable event feed, and how to bring the history of cases in from another help desk.
Where to start - Create an API key in Settings, under API and developers. The key resolves your account and scopes everything to it. - Every call authenticates the same way as the rest of the API: Authorization: Bearer followed by your key. - Everything under this article lives under /api/v1/service and is scoped to the ticket object. A key restricted to other objects does not reach it. - Every endpoint has a matching tool on the MCP server, so an AI agent connected to SellioCRM can do exactly the same things.
Cases - GET /api/v1/service/tickets lists cases as flat rows, filtered by status, priority, queue, owner or free text. Use updated_since with a timestamp to pull only what changed since your last sync, and limit and offset to page. - GET /api/v1/service/tickets/{id} returns one case with the SLA clock: the due date, whether it is paused, the first response target and the next response target. That clock is the reason this endpoint exists; the generic record endpoint returns the fields, not the deadlines. - POST /api/v1/service/tickets opens a case through the same path the console uses, so the protocol number, the SLA targets, the routing rules and the outbound webhooks all happen. - PATCH /api/v1/service/tickets/{id} changes status, priority, category, queue and the assignee. Assigning is the same verb as changing status, so there is no separate endpoint for it. - GET and POST /api/v1/service/tickets/{id}/messages read and write the thread. kind public replies to the customer and sends the email; kind internal leaves a note only agents see. - POST /api/v1/service/tickets/merge merges two cases: the secondary is absorbed by the primary and its messages move over.
Around the case - GET /api/v1/service/queues lists the queues with capacity per agent, business hours and default priority. Read it before routing: the queue name is what the create call expects. - GET and POST /api/v1/service/catalog read the service catalog storefront and request an item. A request opens a real case with the queue and the priority of the item; send your own idempotencyKey so a retry returns the existing request instead of opening a second case. - GET /api/v1/service/entitlements returns support entitlements per account or contract, with the remaining balance. - GET /api/v1/service/surveys returns satisfaction survey invitations with their state and answer date. Anonymous surveys never return the respondent.
Events, in real time and by cursor An integration needs both halves of real time: to be told the instant something happens, and to catch up on what it missed while it was down. You get the first from a subscription and the second from the feed.
- POST /api/v1/service/subscriptions registers a URL and the events you care about. events takes exact types or a family wildcard such as ticket.* An unknown event is refused by name, because a subscription that never fires is worse than an error. The secret comes back once; copy it.
- Every delivery arrives as a POST with three headers: x-sellio-event with the event name, x-sellio-delivery with the id of that attempt, and x-sellio-signature in the form sha256 followed by the code computed over the exact body with your secret. Recompute it on your side and compare; if it does not match, discard the call.
- A delivery that fails is retried with growing waits, roughly one minute, five, thirty, two hours and six hours. After that it is marked dead and stays in the log.
- GET /api/v1/service/deliveries is the consultable answer to did this event arrive. Each line carries the attempts, the last status code, the error and the next scheduled retry.
- POST /api/v1/service/deliveries/{id}/replay re-sends one delivery. It replays the stored body, byte for byte the same that went out the first time. Rebuilding the payload now would send today's state with yesterday's timestamp.
- GET /api/v1/service/events is the feed. Pass the cursor from the previous answer and you get only what happened after that point. nextCursor comes back even on an empty page, and it is what you store. Treat the cursor as opaque: doing arithmetic with it is how an integration breaks silently later.
- GET /api/v1/service/event-types lists every event the product can send, which of the three webhook registries can subscribe to it, and whether reaching a service subscription needs the event bus turned on for your account. Read it before creating a subscription.
Exporting cases and messages GET /api/v1/service/export takes entity tickets or messages and format json or csv. The messages export is the reason this endpoint exists: the thread is not a record, so no other surface delivered it in bulk. Filter by since, status, queue, ticketId or kind, and page with limit, up to 500, and offset. The json answer carries hasMore so you know a page is still waiting.
Bringing history in from another help desk POST /api/v1/service/import takes up to 500 rows per call. Each row needs externalKey, which is the case number in the tool you are leaving, and a subject. It may also carry body, requester email, name and phone, priority, category, queue, channel, status and createdAt.
- The import is idempotent by externalKey. Importing the same file twice reports the rows as existing instead of duplicating them, so you can rerun a failed batch without fear.
- The case is born with the date and the status from the source. Without that, fifteen thousand closed cases would land as new on today's date, flooding the queue of the people working right now and lying in every report by period.
- Send dryRun true to see what would happen without writing anything.
- A row that fails validation becomes an entry in problems with its position in the batch, and the rest of the batch goes in. A bad date in three rows does not force you to start over.