ChaosDesk ChaosDesk

API reference

Two authenticated surfaces: an ingest API for your application, and an agent API for tooling.

Authentication

The ingest API authenticates with a per-site token in the X-Site-Token header. It is a server-side secret and is stored hashed; ChaosDesk cannot show it to you again after it is issued.

X-Site-Token: your-ingest-token

The agent API authenticates with a team API token as a bearer token. Tokens may be scoped to a single site.

Authorization: Bearer your-team-token

Rate limits

Ingest is limited to 30 requests per minute per caller and 120 per minute per site. Everything else is limited to 60 requests per minute. Exceeding a limit returns 429.

Ingest endpoints

GET /api/v1/public/config

Returns the site name plus its active categories, priorities and author-visible custom fields. Use it to build your form.

POST /api/v1/public/tickets

Creates a ticket. Returns the ticket and a per-ticket access_token, which is the credential for reading and replying to that thread.

{
  "email": "[email protected]",     // required
  "name": "Ada Lovelace",              // optional
  "subject": "Cannot save my settings", // required, max 255
  "message": "It reverts every time.",  // required, max 10000
  "category_id": 3,                     // optional, must belong to the site
  "priority_id": 2,                     // optional, defaults to the site default
  "custom_fields": {"plan": "pro"},     // optional
  "context": { ... }                    // optional, see below
}

GET /api/v1/public/tickets/{ulid}?access_token=…

Returns the ticket with its public messages and attachments.

POST /api/v1/public/tickets/{ulid}/messages?access_token=…

Adds a customer reply. A resolved or closed ticket reopens.

POST /api/v1/public/tickets/{ulid}/attachments?access_token=…

Uploads one file as multipart/form-data under the key "file". Accepts jpg, jpeg, png, gif, pdf, doc, docx and txt, up to 10 MB. Attachments are private and served through short-lived URLs.

Context schema

Every group is optional. Unknown keys are dropped rather than stored, so you can send a superset safely. The whole object is shown to agents beside the ticket.

{
  "source": "web | ios | android | macos | api | email",
  "sdk":     {"name": "laravel-chaosdesk", "version": "1.0.0"},
  "app":     {"name": "Acme", "version": "2.4.1", "build": "381", "environment": "production"},
  "runtime": {"php": "8.5.0", "framework": "13.29.0"},
  "device":  {"platform": "ios", "os_version": "26.1", "model": "iPhone17,2",
              "locale": "nl-NL", "timezone": "Europe/Amsterdam"},
  "page":    {"url": "https://acme.test/settings", "referrer": "…",
              "viewport": "1512x832", "user_agent": "…", "locale": "nl"},
  "user":    {"external_id": "4711", "name": "Ada", "email": "[email protected]",
              "plan": "pro", "signed_up_at": "2024-03-01"},
  "console": [{"level": "error", "message": "Uncaught TypeError", "at": "2026-09-01T09:00:00Z"}],
  "extra":   {"anything": "you like"}
}

Limits: string values are truncated at 255 characters, the console buffer holds at most 50 entries of 2000 characters each, and extra is free-form JSON.

Identity

When you send context.user.external_id, ChaosDesk treats the identity as verified: the request arrived over your server-to-server token, so you have already authenticated the person. That id becomes the stable key for the author, surviving email changes on your side.

Agent endpoints

Authenticated with a team API token.

GET    /api/v1/sites
POST   /api/v1/sites                      → returns site_token once
GET    /api/v1/sites/{site}
PATCH  /api/v1/sites/{site}
DELETE /api/v1/sites/{site}

GET    /api/v1/tickets
GET    /api/v1/tickets/{ticket}
PATCH  /api/v1/tickets/{ticket}
POST   /api/v1/tickets/{ticket}/messages
POST   /api/v1/tickets/{ticket}/notes

GET|POST|PATCH|DELETE  /api/v1/sites/{site}/categories
GET|POST|PATCH|DELETE  /api/v1/sites/{site}/priorities
GET|POST|PATCH|DELETE  /api/v1/sites/{site}/custom-fields
GET|POST|PATCH|DELETE  /api/v1/sites/{site}/webhook-channels

SLA fields

Tickets carry both clocks. A clock is paused while the ticket is pending, if the policy is configured that way, and the deadline moves out by the paused working time when it resumes.

first_response_due_at        first_response_at
first_response_warned_at     first_response_breached_at
resolution_due_at            resolved_at
resolution_warned_at         resolution_breached_at
sla_paused_at                sla_paused_minutes