REST API overview

A small, predictable JSON API for reading and answering conversations, built for automations and AI agents.

Basics

  • Base URL: https://chatmesh.ascend-digital.co.uk. All endpoints are versioned under /v1.
  • JSON in and out. Timestamps are ISO-8601 strings; IDs are UUIDs.
  • HTTPS only.

Authentication

Automations and AI agents use a workspace API key created in Settings → AI agents:

Header
Authorization: Bearer cm_sk_...

API keys can call /v1/tickets*, /v1/stats and /mcp. Messages sent with a key are recorded as authorType: "ai", with the key’s name as the author. Keep keys server-side — never ship them to a browser. Each key counts as one AI agent towards your plan.

The agent portal uses a secure session cookie, and the widget uses a short-lived visitor token; you don’t need either to integrate.

Errors & pagination

Errors use standard HTTP status codes plus a JSON body with a stable machine-readable code:

Error
HTTP/1.1 404 Not Found
Content-Type: application/json

{ "error": { "code": "not_found", "message": "Ticket not found" } }

Common codes: bad_request, validation_failed, unauthorized, forbidden, not_found, conflict, plan_limit, rate_limited.

List endpoints accept ?cursor=&limit= and return { items, nextCursor }. When nextCursor is null, you’ve reached the end.

Tickets & stats

EndpointDescription
GET /v1/ticketsList tickets, newest activity first. Filters: status=open|pending|closed, assignee=me|unassigned|<userId>, q, cursor, limit.
GET /v1/tickets/:idA ticket and all its messages, including internal notes.
POST /v1/tickets/:id/messagesAdd a message: { body, internal? }. Replying to a closed ticket reopens it.
PATCH /v1/tickets/:idUpdate { status?, priority?, assigneeId?, subject? }.
DELETE /v1/tickets/:idAdmin/owner only. Permanently erases the conversation, its messages and (if they have no other conversations) the visitor record — useful for erasure requests.
GET /v1/statsOpen, pending, closed in the last 7 days, new this month and median first-response time.
List open tickets
curl "https://chatmesh.ascend-digital.co.uk/v1/tickets?status=open&limit=2" \
  -H "Authorization: Bearer cm_sk_..."

{
  "items": [
    {
      "id": "6f1c…", "number": 1042, "subject": "Does the Startup plan include Slack?",
      "status": "open", "priority": "normal", "assignee": null,
      "visitor": { "id": "…", "name": null, "email": "sam@example.com" },
      "sourceUrl": "https://example.co.uk/pricing",
      "lastMessagePreview": "Does the Startup plan include Slack?",
      "channels": { "slack": true, "discord": false },
      "createdAt": "2026-11-04T09:12:44.000Z", "updatedAt": "…", "lastMessageAt": "…"
    }
  ],
  "nextCursor": "eyJ…"
}
Reply, note and update
curl -X POST "https://chatmesh.ascend-digital.co.uk/v1/tickets/6f1c…/messages" \
  -H "Authorization: Bearer cm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "body": "Yes — Slack is included on Startup and Enterprise." }'

# Internal note (never shown to the visitor)
curl -X POST "https://chatmesh.ascend-digital.co.uk/v1/tickets/6f1c…/messages" \
  -H "Authorization: Bearer cm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "body": "Customer is evaluating for 4 seats.", "internal": true }'

# Hand off to a human
curl -X PATCH "https://chatmesh.ascend-digital.co.uk/v1/tickets/6f1c…" \
  -H "Authorization: Bearer cm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "priority": "high", "status": "open" }'

Objects

Ticket

id, number, subject, status (open | pending | closed), priority (low | normal | high | urgent), assignee, visitor, sourceUrl, lastMessagePreview, channels (slack, discord), createdAt, updatedAt, lastMessageAt.

Message

id, ticketId, authorType (visitor | agent | ai | system), authorName, body, internal, via (widget | portal | slack | discord | mcp | api), createdAt.

Message bodies are plain text, up to 5,000 characters. Always render them as text, never as HTML.

Webhooks

Startup and Enterprise workspaces can register one HTTPS endpoint. ChatMesh sends POST requests with { type, ticket, message? }, where type is ticket.created, message.created or ticket.updated.

Each request is signed: X-ChatMesh-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<rawBody>")>. See the verification example in Node.js.

Realtime

The portal and widget receive live updates over WebSockets (ticket.created, ticket.updated, message.created and typing events), with a ping every 25 seconds. For server-side integrations, use webhooks instead.

MCP

POST https://chatmesh.ascend-digital.co.uk/mcp — Streamable HTTP transport, authenticated with Authorization: Bearer cm_sk_.... Tools: list_tickets, get_ticket, reply_to_ticket, add_internal_note, update_ticket, get_stats.

For stdio-only clients, run npx -y @chatmesh/mcp with CHATMESH_API_KEY set (and optionally CHATMESH_API_URL). Setup steps are in the getting started guide.

Other endpoints

Workspace settings, team members, API keys, integrations and billing are managed through the agent portal at , which uses the same API with a signed-in session. Public widget endpoints are used by widget.js and are not intended to be called directly.

Need something that isn’t covered? Email support@chatmesh.io.