Appearance
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
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health | — | Liveness. Always 200 if the process is up. |
| GET | /ready | — | Readiness. Checks DB + Intercom breaker. 200 / 503. |
Conversations
| Method | Path | Description |
|---|---|---|
| GET | /api/conversations | List the user's conversations (summaries). |
| POST | /api/conversations | Start a request. Body { body?, reason?, meta? } — see below. |
| GET | /api/conversations/:id | Full conversation: messages + state + rating. |
| GET | /api/conversations/:id/poll?since=<ms> | New messages and live state (below). |
| POST | /api/conversations/:id/read | Mark the conversation read (clears the unread badge). |
| POST | /api/conversations/:id/close | Close the request (user-initiated). Triggers the CSAT prompt. |
| POST | /api/conversations/:id/reply | Send a message. Body { body, attachmentIds? }. |
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.
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,
"rated": false // true once a CSAT tag is on the conversation
}Tickets
| Method | Path | Description |
|---|---|---|
| GET | /api/tickets | List the user's tickets (newest first; includes rated). |
| POST | /api/tickets | Create a ticket. Body { title, description } → 201 with the ticket. |
| GET | /api/tickets/search?q=AW-123 | Filter by reference. |
| GET | /api/tickets/:id | Ticket detail: messages + status timeline. |
| POST | /api/tickets/:id/reply | Reply. Reopens the ticket if it was resolved. |
| POST | /api/tickets/:id/clarify | Route a "clarify status" message to the chat. |
Files
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/files/upload | Bearer | multipart/form-data (file). Validates, then S3/local. |
| GET | /api/files/:id | Bearer | Streams the bytes (owner-checked) under the verified content-type. |
| GET | /files/:id?t=<hmac> | public | Signed URL so Intercom can fetch a user's attachment (it sends no auth header). |
Upload validation: size cap MAX_FILE_MB (default 20 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)
| Method | Path | Body | Description |
|---|---|---|---|
| POST | /api/csat | { stars 1–5, comment?, ticketId?, conversationId? } | Idempotent per (user, ticket) — or per (user, conversation) when there's no ticket; duplicate → { id: null, duplicate: true }. |
| POST | /api/complaints | { category, description (≥1), chatId?, dialogId? } | Stores a complaint (description is free text; reasons are admin-managed). |
Complaint categories: funds · security · operator · technical · other.
Devices & settings
| Method | Path | Description |
|---|---|---|
| POST | /api/devices/register | { platform: "mobile"|"tma", token }. Upserts. |
| DELETE | /api/devices/:id | Unregister (on logout). |
| GET | /api/settings/notifications | Preferences (defaults all-on). |
| PUT | /api/settings/notifications | { chatMessages?, ticketUpdates?, systemNotices?, channelPosts? }. |
Diagnostics
| Method | Path | Description |
|---|---|---|
| POST | /api/diagnostics | Log device telemetry (no PII). 204. |
Config
| Method | Path | Description |
|---|---|---|
| GET | /api/config | Admin-managed complaint / CSAT / request-topic reason lists the SDK renders. |
AW Channel (client, read-only feed)
| Method | Path | Description |
|---|---|---|
| GET | /api/channel/list | Channel 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/view | Record 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/read | Clear 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.
| Method | Path | Description |
|---|---|---|
| GET | /api/channel/:id/posts/:postId/comments?cursor= | Paged thread (visible comments, newest-first). |
| POST | /api/channel/:id/posts/:postId/comments | Create. 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/report | Flag a comment. Body { reasonId?, note? }. |
Admin moderation endpoints (moderate_comments permission):
| Method | Path | Description |
|---|---|---|
| GET | /admin/channel/comments?filter=all|reported|filtered|hidden|deleted&postId= | Moderation queue. filtered = blocked attempts (secret redacted). |
| GET | /admin/channel/posts/:id/comments | All comments on a post (post detail view). |
| POST | /admin/channel/comments/:id/hide | /restore | /delete | Change a comment's status. |
| POST | /admin/channel/comments/moderate-user | Body { 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
| Method | Path | Description |
|---|---|---|
| POST | /webhooks/intercom | Intercom events. Public; HMAC-verified. See Webhooks. |
Error codes
| Status | error | When |
|---|---|---|
| 400 | validation_error / bad_request | Bad input. |
| 401 | unauthorized | Missing/invalid/expired token. |
| 429 | rate_limited | Per-user rate limit (see Retry-After). |
| 502 | service_unavailable | Intercom down / circuit open. |
| 500 | internal_error | Unexpected. |