APIs / Helpdesk
A support desk: tickets, messages, agents, customers, SLAs — the state-machine one.
Two thousand tickets with their message threads, twenty-five agents in four teams and three hundred customer companies. A ticket moves open → pending → resolved → closed; PATCH its status and the hub checks the transition and stamps the timestamps. 7,398 records in all.
In Sonda: Import → From a URL with the OpenAPI address and the whole API lands as a project, one request per operation with example bodies. No keys, no headers to add. More on each protocol.
_note and X-Sondahub-Write: simulated. A GET afterwards will not find what you wrote.curl https://api.sondahub.com/v1/helpdesk
Every list answers { "data": [...], "meta": { "page", "limit", "total", "pages" } } with X-Total-Count and Link headers (next, prev, first, last). These options work on every collection and every nested route:
| Option | Meaning | Example |
|---|---|---|
page, limit | Paging, 1-based; limit 1–200, default 20. offset works too. | ?page=3&limit=50 |
sort | Comma list of fields, - for descending. Default id here. | ?sort=-id,id |
field=value | Equals. Booleans as true/false, null for missing. | ?id=1 |
_ne _gt _gte _lt _lte | Not equal and comparisons, on numbers, dates and strings. | ?id_gt=10 |
_like | Contains, case-insensitive. | ?name_like=an |
_in | Any of a comma list. | ?id_in=1,2,3 |
_null | true: missing; false: present. | ?slug_null=true |
a.b=value | Inside a JSON field, dotted. | ?attachments.name=… |
q | Search across the text fields. | ?q=alpine |
fields | Only these fields back. | ?fields=id,name |
expand | Embed related records. | ?expand=agents |
A name that is not a field answers 400 and lists the fields. Writes answer 422 with one line per problem, 404 for a missing id, 405 with an Allow header for a verb a route does not take.
Support teams. 4 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
namerequired | string | |
slug | string | |
timezone | string | |
hours | string |
Relations: agents → the agents whose team_id is this team. Use ?expand=agents to embed them, or the routes below.
curl "https://api.sondahub.com/v1/helpdesk/teams?limit=3"
curl https://api.sondahub.com/v1/helpdesk/teams/1
curl "https://api.sondahub.com/v1/helpdesk/teams/1/agents?limit=5"
curl -X POST https://api.sondahub.com/v1/helpdesk/teams \
-H "Content-Type: application/json" \
-d '{"name":"Tier 1","hours":"24/7"}'
curl -X PATCH https://api.sondahub.com/v1/helpdesk/teams/1 \
-H "Content-Type: application/json" \
-d '{"name":"Changed name"}'
curl -X DELETE https://api.sondahub.com/v1/helpdesk/teams/1
The people answering tickets. 25 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
namerequired | string | |
emailrequired | string | |
team_idrequired | int → teams | |
role | enum | agent senior lead admin |
status | enum | available busy away offline |
skills | json string[] | |
open_ticketsread-only | int | |
ratingread-only | float |
Relations: team → one team through team_id; tickets → the tickets whose assignee_id is this agent. Use ?expand=team,tickets to embed them, or the routes below.
curl "https://api.sondahub.com/v1/helpdesk/agents?role=senior&expand=team&limit=3"
curl https://api.sondahub.com/v1/helpdesk/agents/1?expand=team
curl "https://api.sondahub.com/v1/helpdesk/agents/1/tickets?limit=5"
curl -X POST https://api.sondahub.com/v1/helpdesk/agents \
-H "Content-Type: application/json" \
-d '{"name":"A name","email":"A email","team_id":1,"role":"agent","status":"available"}'
curl -X PATCH https://api.sondahub.com/v1/helpdesk/agents/1 \
-H "Content-Type: application/json" \
-d '{"role":"senior"}'
curl -X DELETE https://api.sondahub.com/v1/helpdesk/agents/1
Companies with a support contract. 300 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
namerequired | string | |
domain | string | |
plan | enum | free starter business enterprise |
contact_name | string | |
contact_email | string | |
sla_hours | int | First response target, hours. |
open_ticketsread-only | int | |
satisfactionread-only | float | Average CSAT, 1–5. |
Relations: tickets → the tickets whose customer_id is this customer. Use ?expand=tickets to embed them, or the routes below.
curl "https://api.sondahub.com/v1/helpdesk/customers?plan=starter&sla_hours_gte=1&limit=3"
curl https://api.sondahub.com/v1/helpdesk/customers/1
curl "https://api.sondahub.com/v1/helpdesk/customers/1/tickets?limit=5"
curl -X POST https://api.sondahub.com/v1/helpdesk/customers \
-H "Content-Type: application/json" \
-d '{"name":"Blue Systems Inc.","domain":"bluesystems.example","plan":"free"}'
curl -X PATCH https://api.sondahub.com/v1/helpdesk/customers/1 \
-H "Content-Type: application/json" \
-d '{"plan":"starter"}'
curl -X DELETE https://api.sondahub.com/v1/helpdesk/customers/1
A support request. Allowed status moves: open → pending | resolved; pending → open | resolved; resolved → closed | open; closed → open (reopen). Anything else answers 422. 2,000 records — the file.
invalid_transition otherwise; resolving and closing stamp their timestamps, assigning stamps first_response_at. Counters on the customer and agent follow.| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
numberread-only | string | |
subjectrequired | string | |
description | text | |
status | enum | open pending resolved closed |
priority | enum | low normal high urgent |
category | enum | billing account bug feature howto |
channel | enum | email chat phone web api |
customer_idrequired | int → customers | |
requester_email | string | |
assignee_id | int → agents | |
team_id | int → teams | |
tags | json string[] | |
first_response_atread-only | datetime | |
resolved_atread-only | datetime | |
closed_atread-only | datetime | |
due_at | datetime | |
sla_breachedread-only | bool | |
satisfaction | int | CSAT given at close. min 1, max 5 |
message_countread-only | int |
Relations: customer → one customer through customer_id; assignee → one agent through assignee_id; team → one team through team_id; messages → the messages whose ticket_id is this ticket. Use ?expand=customer,assignee,team,messages to embed them, or the routes below.
curl "https://api.sondahub.com/v1/helpdesk/tickets?status=pending&satisfaction_gte=1&expand=customer&limit=3"
curl https://api.sondahub.com/v1/helpdesk/tickets/1?expand=customer
curl "https://api.sondahub.com/v1/helpdesk/tickets/1/messages?limit=5"
curl -X POST https://api.sondahub.com/v1/helpdesk/tickets \
-H "Content-Type: application/json" \
-d '{"subject":"A subject","status":"open","priority":"low","category":"billing","channel":"email","customer_id":1}'
curl -X PATCH https://api.sondahub.com/v1/helpdesk/tickets/1 \
-H "Content-Type: application/json" \
-d '{"status":"pending"}'
curl -X DELETE https://api.sondahub.com/v1/helpdesk/tickets/1
The thread on a ticket, in order. author_type says who wrote it. 5,069 records — the file.
message_count; an agent’s first message stamps first_response_at; a customer message reopens a pending ticket.| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
ticket_idrequired | int → tickets | |
author_typerequired | enum | customer agent system |
author_id | int → agents | The agent, when author_type is agent. |
author_name | string | |
bodyrequired | text | |
internal | bool | A private note the customer does not see. |
attachments | json { name, size, content_type }[] |
Relations: ticket → one ticket through ticket_id; author → one agent through author_id. Use ?expand=ticket,author to embed them, or the routes below.
curl "https://api.sondahub.com/v1/helpdesk/messages?author_type=agent&expand=ticket&limit=3"
curl https://api.sondahub.com/v1/helpdesk/messages/1?expand=ticket
curl -X POST https://api.sondahub.com/v1/helpdesk/messages \
-H "Content-Type: application/json" \
-d '{"ticket_id":1,"author_type":"customer","body":"A body"}'
curl -X PATCH https://api.sondahub.com/v1/helpdesk/messages/1 \
-H "Content-Type: application/json" \
-d '{"author_type":"agent"}'
curl -X DELETE https://api.sondahub.com/v1/helpdesk/messages/1
The same stream two ways: the world's own activity, one tick a second, generated for your connection alone. Both push JSON text messages; SSE names each one with event: and numbers it with id:. ?topics=a,b narrows either.
| Topic | What arrives | How often |
|---|---|---|
tickets | A ticket opening, being assigned, or changing status. | 5 s |
messages | A new message on a ticket. | 4 s |
wss://api.sondahub.com/v1/helpdesk/ws?topics=tickets
> {"type":"hello","api":"helpdesk","topics":[…],"subscribed":[…]}
> {"type":"event","topic":"tickets","api":"helpdesk","ts":"…","data":{…}}
< {"type":"subscribe","topics":["tickets"]} # narrow to some topics
< {"type":"ping"} # → {"type":"pong"}
< anything else # → echoed back as {"type":"echo"}
curl -N "https://api.sondahub.com/v1/helpdesk/events?topics=tickets"
retry: 3000
id: 1
event: tickets
data: {"type":"event","topic":"tickets",…}
One endpoint, https://api.sondahub.com/v1/helpdesk/graphql: POST {"query", "variables"} or GET ?query=. Introspection is on, so Sonda's GraphQL mode loads the schema; the SDL is a click away. Every collection is a paged query with the same filter, sort and q options as REST (operators as suffixes: price_lt), a by-id query, relation fields both ways, and create, update, replace and delete mutations — simulated like every write, with the note in extensions.
curl https://api.sondahub.com/v1/helpdesk/graphql -H "Content-Type: application/json" -d '{"query": "{ agents(limit: 3, sort: \"-id\", filter: { role: agent }) { total data { id name email team_id team { name } tickets(limit: 2) { id } } } }"}'
{
agents(limit: 3, sort: "-id", filter: { role: agent }) {
total
data {
id name email team_id
team { name }
tickets(limit: 2) { id }
}
}
}