Search the docs

Type to find a guide or an answer, then use the arrow keys and Enter to open it.

Help Desk API

Read and work your SiteSmith Help Desk tickets, customers and knowledge base from your own code.

Overview#

The Help Desk API lets your own code list, open, create and update tickets, reply to customers, add internal notes, look up customers, and read your knowledge base.

  • Base URL: https://api.sitesmith.cloud/v1/helpdesk
  • Bodies are JSON (Content-Type: application/json). Responses are JSON.
  • Dates are ISO 8601 strings in UTC, like 2026-09-29T10:00:00.000Z.
  • Bodies over 256 KB are refused with 413.

Authentication#

Create a key in your help desk's agent console under Settings › API. You need a role that can manage API keys. The key is shown once, so copy it somewhere safe. Send it in the Authorization header:

bash
curl https://api.sitesmith.cloud/v1/helpdesk/tickets \
  -H "Authorization: Bearer ssh_your_key_here"

A key reaches one help desk only, and acts with the permissions of the person who created it. If that person leaves the desk or the account, the key stops working.

ScopeWhat it can do
readGET requests only. Anything else returns 403.
writeEverything below.

Warning

Keep keys on your server. CORS is open to any origin (without cookies), but anyone who can see your page's code can copy a key used in the browser.

Errors#

Errors return a JSON body with a message, and field messages when custom field values fail validation:

json
{ "error": "Fill in Order number", "fieldErrors": { "fld123": "Fill in Order number" } }
StatusMeaning
400The request is malformed (bad JSON, an unknown status, per_page over 100).
401No key, or the key is wrong or revoked.
402Your plan's limit is reached, or the ticket came in over the monthly limit and is held until you upgrade.
403A read key tried to write, or the key's owner isn't allowed to do that.
404The ticket, customer or article doesn't exist, or belongs to another desk.
409The ticket is closed. Replies to closed tickets aren't allowed; open a follow-up instead.
413The body is over 256 KB.
422Custom field values failed validation. See fieldErrors, keyed by field id.
429Too many requests. Wait the number of seconds in the Retry-After header.

Rate limits#

Each key can make 120 requests a minute.

Tickets#

A ticket looks like this:

json
{
  "id": "k3Jd9sQw1a",
  "number": 42,
  "subject": "Can't sign in",
  "status": "open",
  "priority": "normal",
  "type": null,
  "channel": "email",
  "requesterId": "r8Hs2kLm0p",
  "assigneeId": null,
  "groupId": null,
  "tags": ["login"],
  "fields": {},
  "ccs": [],
  "followers": [],
  "dueAt": null,
  "firstResponseAt": null,
  "solvedAt": null,
  "closedAt": null,
  "firstResponseDueAt": "2026-09-29T14:00:00.000Z",
  "resolutionDueAt": null,
  "firstResponseBreachedAt": null,
  "resolutionBreachedAt": null,
  "mergedIntoId": null,
  "followUpOfId": null,
  "spam": false,
  "csatRating": null,
  "createdAt": "2026-09-29T10:00:00.000Z",
  "updatedAt": "2026-09-29T10:05:00.000Z"
}

Statuses are new, open, pending, on_hold, solved and closed. Priorities are low, normal, high and urgent. Custom field values in fields are keyed by field id.

List tickets#

GET /tickets

QueryMeaning
viewA view id. Uses the view's filters and sort instead of the ones below.
statusOne status, or a comma list like new,open.
priorityOne priority, or a comma list.
assignee, group, requesterAn id, or none for tickets without one.
tagA tag. Repeat it to require several.
updated_sinceA date, like 2026-09-01.
sortupdatedAt (default), createdAt, priority, number, status, dueAt or nextSlaAt.
dirdesc (default) or asc.
pageFrom 0.
per_page1 to 100 (default 50).

Spam, merged and held tickets are never listed.

bash
curl "https://api.sitesmith.cloud/v1/helpdesk/tickets?status=new,open&sort=createdAt&per_page=20" \
  -H "Authorization: Bearer ssh_your_key_here"
json
{ "tickets": [ { "id": "k3Jd9sQw1a", "number": 42, "requesterName": "Ada Lovelace", "requesterEmail": "[email protected]", "assigneeName": null, "groupName": null } ], "total": 1, "page": 0, "perPage": 20, "hasMore": false }

Each row is a full ticket plus requesterName, requesterEmail, assigneeName and groupName.

Get a ticket#

GET /tickets/{id}

Returns ticket, requester, org (or null), every messages entry (public replies and internal notes, oldest first), and followUpOf, mergedInto and followUps links.

List a ticket's messages#

GET /tickets/{id}/messages

Add ?kind=reply for public replies only, or ?kind=note for internal notes only.

json
{
  "messages": [
    {
      "id": "m1",
      "ticketId": "k3Jd9sQw1a",
      "kind": "reply",
      "authorKind": "requester",
      "authorUserId": null,
      "requesterId": "r8Hs2kLm0p",
      "authorName": "Ada Lovelace",
      "body": "I can't sign in since this morning.",
      "html": null,
      "via": "email",
      "delivery": "none",
      "createdAt": "2026-09-29T10:00:00.000Z",
      "attachments": []
    }
  ]
}

Create a ticket#

POST /tickets (write key)

bash
curl https://api.sitesmith.cloud/v1/helpdesk/tickets \
  -H "Authorization: Bearer ssh_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Refund for order 1001","body":"Hi Ada, we have started your refund.","requesterEmail":"[email protected]","requesterName":"Ada Lovelace","priority":"high","tags":["billing"]}'
FieldNotes
subject, bodyRequired.
requesterEmailRequired unless you send requesterId. requesterName is optional.
firstMessagereply (default, emailed to the customer) or note (internal).
priority, type, status, groupId, assigneeId, tags, fields, ccs, dueAtOptional. Required custom fields must be filled.

Returns 201 { "ticket": { … } }. The ticket's channel is api.

Update a ticket#

PATCH /tickets/{id} (write key)

Send any of subject, status, priority, type, assigneeId, groupId, tags, fields, ccs, followers, dueAt and requesterId. null clears a value. Triggers run just as they do for an agent's change, and solving a ticket sends the satisfaction survey when it's turned on.

bash
curl -X PATCH https://api.sitesmith.cloud/v1/helpdesk/tickets/k3Jd9sQw1a \
  -H "Authorization: Bearer ssh_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"status":"pending","tags":["billing","refund"]}'

Returns 200 { "ticket": { … } }.

Reply to the customer#

POST /tickets/{id}/replies (write key)

Body: { "body": "…", "status"?: "pending", "ccs"?: ["…"] }. The reply is emailed to the requester and anyone copied. status sets the ticket's status as the reply is sent; without it, a new ticket becomes open. Returns 201 { "message": { … }, "ticket": { … } }. Closed tickets return 409.

Add an internal note#

POST /tickets/{id}/notes (write key)

Body: { "body": "…" }. Customers never see notes. Mention an agent with <@userId> to notify them. Returns 201 { "message": { … }, "ticket": { … } }.

Customers#

List customers#

GET /requesters

Query: q (name or email), org (an organization id), limit (1 to 500, default 100).

json
{ "requesters": [ { "id": "r8Hs2kLm0p", "email": "[email protected]", "name": "Ada Lovelace", "orgId": null, "orgName": null, "ticketCount": 3, "blocked": false, "createdAt": "2026-09-01T09:00:00.000Z", "updatedAt": "2026-09-01T09:00:00.000Z" } ] }

Get a customer#

GET /requesters/{id}

Returns { "requester": { … } }.

Create a customer#

POST /requesters (write key)

Body: { "email": "…", "name"?: "…", "orgId"?: "…" }. If a customer with that email already exists, you get that one back. Returns 201 { "requester": { … } }.

Knowledge base#

List or search articles#

GET /articles

Without a query, returns every article in knowledge base order, drafts included, each with its section and category. With ?q=, returns the best matches first, each with a rank.

json
{ "articles": [ { "id": "a1", "sectionId": "s1", "title": "Resetting your password", "slug": "resetting-your-password", "excerpt": "…", "status": "published", "visibility": "public", "helpfulYes": 12, "helpfulNo": 1, "views": 340, "publishedAt": "2026-09-01T09:00:00.000Z", "createdAt": "2026-09-01T09:00:00.000Z", "updatedAt": "2026-09-10T09:00:00.000Z", "rank": 0.42 } ] }

visibility is public or signed_in (only signed-in customers see it in your help center).

Get an article#

GET /articles/{id}

Returns { "article": { …, "body": "<p>…</p>" } }. The body is sanitized HTML.

Still stuck?

A real person will take it from here.