Skip to content

API reference ​

Base URL: your serverUrl. All /api/* endpoints require Authorization: Bearer <session-jwt>. Responses are JSON; errors are { error, message } with an appropriate status.

The SDK calls these for you — this reference is for debugging and for teams extending the backend.

Health ​

MethodPathAuthDescription
GET/health—Liveness. Always 200 if the process is up.
GET/ready—Readiness. Checks DB + Intercom breaker. 200 / 503.

Conversations ​

MethodPathDescription
GET/api/conversationsList the user's conversations (summaries).
POST/api/conversationsStart a request. Body { body?, reason?, meta? } — see below.
GET/api/conversations/:idFull conversation: messages + state + rating.
GET/api/conversations/:id/poll?since=<ms>New messages and live state (below).
POST/api/conversations/:id/readMark the conversation read (clears the unread badge).
POST/api/conversations/:id/closeClose the request (user-initiated). Triggers the CSAT prompt.
POST/api/conversations/:id/replySend a message. Body { body, attachmentIds? }. Reopens a closed conversation inside the reopen window; 409 reopen_window_expired after it.

Starting a request — body is optional. If you pass a reason ({ main, sub? }) with no body, the backend opens the conversation from the topic alone (the picked topic is recorded as an internal note/tag, not shown as a user message). meta carries browser/IP-style context used to enrich the Intercom contact. At least one of body / reason is required.

Reopen window — Intercom's Conversations API reopens on any user reply and does not expose Messenger's day setting. The backend mirrors it with INTERCOM_REOPEN_WINDOW_DAYS (default 5; 0 = never). Full conversation and poll responses include reopenUntil (epoch ms) while state is closed.

Poll response — besides messages, the poll returns live conversation state so operator-side changes reflect within one interval without a reload:

json
{
  "messages": [ /* … newer than `since` */ ],
  "latestTimestamp": 1719750000,
  "state": "open",          // "open" | "closed" | "snoozed"
  "snoozedUntil": null,
  "reopenUntil": null,      // epoch ms while closed; null when N/A or unknown
  "rated": false            // true for the current close-cycle's CSAT
}

Tickets ​

MethodPathDescription
GET/api/ticketsList the user's tickets (newest first; includes rated).
POST/api/ticketsCreate a ticket. Body { title, description } → 201 with the ticket.
GET/api/tickets/search?q=AW-123Filter by reference.
GET/api/tickets/:idTicket detail: messages + status timeline.
POST/api/tickets/:id/replyReply. Reopens the ticket if it was resolved.
POST/api/tickets/:id/clarifyRoute a "clarify status" message to the chat.

Files ​

MethodPathAuthDescription
POST/api/files/uploadBearermultipart/form-data (file). Validates, then S3/local.
GET/api/files/:idBearerStreams the bytes (owner-checked) under the verified content-type.
GET/files/:id?t=<hmac>publicSigned URL so Intercom can fetch a user's attachment (it sends no auth header).

Upload validation: size cap MAX_FILE_MB (default 50 MB), count cap MAX_FILES_PER_MSG (default 10), a fixed allow-list (JPEG, PNG, GIF, WEBP, BMP, PDF, MP4, TXT) verified by magic bytes (name/MIME are never trusted), stored under a randomized owner-tagged key and served with X-Content-Type-Options: nosniff.

CSAT & complaints (bypass Intercom → our DB) ​

MethodPathBodyDescription
POST/api/csat{ stars 1–5, comment?, ticketId?, conversationId? }Tickets: idempotent per (user, ticket). Conversations: one row per close-cycle (kept for admin history); after a reopen a new row is inserted. Ratings live only in our DB — nothing is written to Intercom. Same-cycle duplicate → { id: null, duplicate: true }. Supplied ticketId/conversationId must belong to the caller → else 403 (no row).
POST/api/complaints{ category, description (≥1), chatId?, dialogId? }Stores a complaint (description is free text; reasons are admin-managed). Soft cooldown 1 / 60s per user — a repeat within the window returns 201 { id, duplicate: true } (no new row) so the UI can still show “sent”. Supplied chatId / dialogId must belong to the caller → else 403.

Complaint categories: funds · security · operator · technical · other.

Devices & settings ​

MethodPathDescription
POST/api/devices/register{ platform: "mobile"|"tma", token }. Upserts.
DELETE/api/devices/:idUnregister. Scoped to the JWT user; missing/foreign id → 404.
GET/api/settings/notificationsPreferences (defaults all-on).
PUT/api/settings/notifications{ chatMessages?, ticketUpdates?, systemNotices?, channelPosts? }.

Diagnostics ​

MethodPathDescription
POST/api/diagnosticsLog device telemetry (no PII). 204.

Config ​

MethodPathDescription
GET/api/configAdmin-managed complaint / CSAT / request-topic reason lists the SDK renders.

AW Channel (client, read-only feed) ​

MethodPathDescription
GET/api/channel/listChannel rows for the hub (computed preview / updatedAt / unread).
GET/api/channel/:id/posts?cursor=&limit=Paged feed (published posts) + pinned posts.
POST/api/channel/:id/posts/:postId/react{ emoji } — single-choice reaction toggle.
POST/api/channel/:id/posts/:postId/viewRecord a view (deduped; bumps the count once).
POST/api/channel/:id/posts/:postId/click{ index } — record an inline-button tap (URL derived server-side).
POST/api/channel/:id/readClear the unread badge.

Reactions/views/clicks verify the post is published and belongs to the channel. The feed also returns commentCount per post (visible comments).

Comments ​

Client endpoints (session JWT). See the Comments guide for the content rules, toggles, and identity/avatar integration.

MethodPathDescription
GET/api/channel/:id/posts/:postId/comments?cursor=Paged thread (visible comments, newest-first).
POST/api/channel/:id/posts/:postId/commentsCreate. Body { body, parentId?, clientId?, authorName?, authorAvatar?, attachment? }. Guards: post/channel toggle, quiet hours, mute/ban, link, DLP, anti-impersonation, rate limit (1 / 8s). Echoes clientId for optimistic reconcile.
POST/api/channel/:id/posts/:postId/comments/:commentId/reportFlag a comment. Body { reasonId?, note? }.

Admin moderation endpoints (moderate_comments permission):

MethodPathDescription
GET/admin/channel/comments?filter=all|reported|filtered|hidden|deleted&postId=Moderation queue. filtered = blocked attempts (secret redacted).
GET/admin/channel/posts/:id/commentsAll comments on a post (post detail view).
POST/admin/channel/comments/:id/hide | /restore | /deleteChange a comment's status.
POST/admin/channel/comments/moderate-userBody { channelId, userId, action: mute|ban|unban, durationHours?, reason? }.

Per-post commentsEnabled, the channel commentsEnabled kill-switch, and quiet hours (quietStart/quietEnd/quietTz) are set via the admin post editor and channel-settings endpoints.

Admin panel (/admin/*, HTTP Basic auth → DB-backed admin users) ​

Separate surface for operators, not the SDK. Auth is admin_users (scrypt) with per-route permission flags (manage_posts, manage_reasons, view_logs, view_feedback, manage_users) + isSuper. Highlights: /admin/login, /admin/me (+ /me/password), /admin/users CRUD, complaint/CSAT/reason management, and the channel authoring API (/admin/channel/* — posts CRUD, media upload, publish/schedule/hide, stats). See routes/admin*.ts for the full list.

Webhooks ​

MethodPathDescription
POST/webhooks/intercomIntercom events. Public; HMAC-verified. See Webhooks.

Error codes ​

StatuserrorWhen
400validation_error / bad_requestBad input.
401unauthorizedMissing/invalid/expired token.
429rate_limitedPer-user rate limit (see Retry-After).
502service_unavailableIntercom down / circuit open.
500internal_errorUnexpected.

AW Chat SDK — integration & platform handoff docs.