{"openapi":"3.1.0","info":{"title":"ChatKomodo API","version":"1.0.0","description":"ChatKomodo — WhatsApp Gateway for Business. A multi-tenant WhatsApp API for any business, in any country: send messages, manage WhatsApp devices, run consent-aware blast campaigns, keep a light CRM, receive webhooks, and bill through Pakasir, Creem or PayPal.\n\n**Authentication**: All `/v1/*` endpoints require `Authorization: Bearer <token>` where the token is either a JWT (2 hour expiry, renewed with a refresh token) or a long-lived API key (a random string shown once when the key is created). Super-admin endpoints (`/v1/admin/*`) require the `X-Admin-Secret` header, plus `X-Admin-Session` once two-factor sign-in is on (see `/v1/admin/2fa`).\n\n**Phone numbers**: use the international format `+<country code><number>`, e.g. `+44 7700 900123` (spaces and dashes are fine). A number written in local format (`07700 900123`) is resolved against the business country from `GET /v1/profile` (or the request's `country_code`). WhatsApp JIDs (`447700900123@s.whatsapp.net`, groups `…@g.us`) are accepted wherever a `chat_id` is expected. Stored numbers are digits with the country code and no `+`.\n\n**Errors and language**: failures return `{ \"error\": \"<message>\", \"code\": \"<CODE>\" }`. The `error` text is English by default and is localized (currently English and Indonesian) when you send `X-Lang: id`, `?lang=id` or `Accept-Language: id`. Always branch on `code` (for example `RECIPIENT_OPTED_OUT`, `CONSENT_ATTESTATION_REQUIRED`, `INVALID_NUMBER`, `CURRENCY_NOT_SUPPORTED`), never on the text.\n\n**Consent**: people who reply STOP (and equivalents in many languages) are opted out automatically. Blasts skip opted-out recipients; 1:1 sends to them succeed with a `RECIPIENT_OPTED_OUT` warning, or fail with 409 when the tenant enabled strict mode. Tenants can require a consent attestation before a blast starts (`CONSENT_ATTESTATION_REQUIRED`).\n\n**Send window**: blast (bulk) messages are only released inside the tenant's daily send window (setting `send_window`, evaluated in the business timezone or each recipient's own timezone). Outside the window they wait; 1:1 replies, scheduled and recurring messages are not held.\n\n**Personalization tokens** in blast, auto-reply, welcome and template texts: `{name}`, `{first_name}`, `{phone}`, `{var1}`…`{var5}`, `{attr.<key>}`. Add a fallback with double braces: `{{first_name|there}}`. A token without a value and without a fallback becomes empty.\n\n**Money**: amounts are integers in minor units of the stated currency (cents; Indonesian rupiah has none).\n\n**Rate limits**: every API key also has its own request limit across all `/v1` endpoints (`rate_limit_per_minute`, default 300, capped by the plan; set it under API keys in the dashboard). It uses a sliding one-minute window, only counts requests made with that key (dashboard sessions are not limited), and every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds until the window rolls over). Going over returns 429 with `code: RATE_LIMITED`, `scope: api_key` and a `Retry-After` header; refused requests are not counted, so waiting that long is enough. On top of that, sending and the other expensive calls have a one-minute window per API key and per endpoint group. `POST` paths containing `messages` follow the plan's send rate (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan); number validation is limited to 10, scheduled messages to 20, blast creation to 5 and bulk operations to 3 per minute. Going over returns 429 with `code: RATE_LIMITED` and a `Retry-After` header (seconds). Messages are also capped by the plan's monthly quota. The limit is on API calls: what WhatsApp actually receives is paced by the device queue, whatever the plan.\n\n**Versioning**: This is v1. Breaking changes will be published under `/v2` with at least 6 months of parallel run.","contact":{"name":"ChatKomodo","url":"https://chatkomodo.com"}},"tags":[{"name":"auth","description":"Sign-up and login with a one-time password sent over WhatsApp"},{"name":"sessions","description":"WhatsApp devices"},{"name":"messages","description":"Sending and reading messages"},{"name":"blast","description":"Broadcast campaigns with CRM audiences, consent checks and the send window"},{"name":"profile","description":"Business profile (country, timezone, language, industry, currency) and public country/industry lists"},{"name":"consent","description":"Opt-in / opt-out records and keyword handling"},{"name":"privacy","description":"Data-subject export / erasure and message retention"},{"name":"crm","description":"Custom contact attributes, tags, segments and per-phone contact cards"},{"name":"contacts","description":"Phone book import and management"},{"name":"settings","description":"Per-tenant settings such as the send window"},{"name":"onboarding","description":"Industry starter packs and the setup checklist"},{"name":"billing","description":"Plans, payment methods (Pakasir, Creem, PayPal), checkout and invoices"},{"name":"inbox","description":"Conversations"},{"name":"auto-reply","description":"Keyword auto-replies"},{"name":"webhooks","description":"Outbound event delivery"},{"name":"realtime","description":"Server-Sent Events"},{"name":"admin","description":"Platform operator endpoints (X-Admin-Secret)"},{"name":"groups","description":"WhatsApp groups of a connected device: list, create, rename, members, invite link and disappearing messages"},{"name":"communities","description":"WhatsApp communities"},{"name":"newsletters","description":"WhatsApp channels (newsletters)"},{"name":"whatsapp-business","description":"WhatsApp Business profile, product catalog and labels (Business accounts)"},{"name":"whatsapp-account","description":"Settings of the linked WhatsApp account: privacy, About text, profile picture, presence and call rejection"},{"name":"blocklist","description":"Recipient blocklist kept by the gateway and contact blocking on WhatsApp itself"},{"name":"status","description":"WhatsApp Status (stories)"},{"name":"api-keys","description":"Child API keys for integrations (managed from a dashboard login, not with an API key)"},{"name":"templates","description":"Reusable message templates"},{"name":"recurring","description":"Messages sent on a daily, weekly, monthly or every-N-days schedule"},{"name":"rotators","description":"Send through a pool of devices (round-robin or random)"},{"name":"scheduled","description":"One-off messages sent at a chosen time"},{"name":"media","description":"Upload, list, download and delete media files used in messages"},{"name":"support","description":"Customer-service agents, the unassigned chat queue and chat assignment"},{"name":"analytics","description":"Dashboard counters, usage against the plan quota, message analytics and CSAT results"},{"name":"audit","description":"Tenant audit log"},{"name":"system","description":"Liveness probe for load balancers and uptime monitors"}],"servers":[{"url":"https://api.chatkomodo.com","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT or API key","description":"Two auth methods, both via the `Authorization: Bearer <token>` header. A JWT (from the OTP / two-factor login) is short-lived (2 hours) and renewed with the rotating refresh token at `POST /auth/refresh`; an API key (a random 43-character string, shown once when created) is long-lived and tenant-scoped."},"adminSecret":{"type":"apiKey","in":"header","name":"X-Admin-Secret","description":"Super-admin only. Required for /v1/admin/* endpoints. Generate with `openssl rand -base64 48`."}},"parameters":{"XLang":{"name":"X-Lang","in":"header","required":false,"description":"Language of the `error` / `message` text in failed (4xx/5xx) JSON responses: `en` (default) or `id`. The query parameter `?lang=` and the standard `Accept-Language` header work the same way (precedence: `?lang=`, `X-Lang`, `Accept-Language`). Field names, enum values and `code`s are never translated.","schema":{"type":"string","enum":["en","id"],"default":"en"}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]},"Session":{"type":"object","properties":{"id":{"type":"string","description":"ULID identifier"},"status":{"type":"string","enum":["pending","qr_ready","connected","disconnected","failed"],"description":"`pending` (starting), `qr_ready` (waiting for a scan), `connected`, `disconnected` (was working, connection lost) or `failed`"},"phone_number":{"type":["string","null"],"description":"Paired WhatsApp number as digits with country code (no `+`), e.g. `447700900123`"},"wa_name":{"type":["string","null"],"description":"WhatsApp display name, read from the phone when it is linked (no need to type it)"},"wa_about":{"type":["string","null"],"description":"The number's WhatsApp \"About\" text (bio), read after linking and refreshed whenever the profile is fetched. `\"\"` = no About; `null` = not read yet or hidden by the phone's privacy settings"},"phone_display":{"type":["string","null"],"description":"The number in international format with its country code, e.g. `+44 7700 900123`"},"phone_country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country of the number, e.g. `GB` (`null` when unknown)"},"phone_country_code":{"type":["string","null"],"description":"Country calling code with `+`, e.g. `+44`"},"api_key_id":{"type":"string","description":"Owning tenant"},"warmup_until_ms":{"type":["integer","null"],"description":"Pacing until this timestamp (anti-ban)"},"created_at":{"type":"integer"},"connected_at":{"type":["integer","null"]}},"required":["id","status","phone_number","wa_name","api_key_id","warmup_until_ms","created_at","connected_at"]},"Message":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"session_id":{"type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"chat_id":{"type":"string","maxLength":64,"pattern":"^[\\d\\-]+(@(s\\.whatsapp\\.net|g\\.us|newsletter|broadcast|lid))?$"},"message_type":{"type":"string","enum":["text","image","document","audio","video","sticker","location","poll","contact"]},"body":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"status":{"type":"string","enum":["queued","sent","delivered","read","failed","cancelled"]},"error":{"type":["string","null"]},"created_at":{"type":"integer"},"wa_message_id":{"type":["string","null"]}},"required":["id","session_id","direction","chat_id","message_type","body","media_r2_key","status","error","created_at","wa_message_id"]},"BlastCampaign":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"session_id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","running","paused","completed","failed","cancelled"]},"body":{"type":["string","null"]},"message_type":{"type":"string"},"total_recipients":{"type":"integer"},"sent_count":{"type":"integer"},"failed_count":{"type":"integer"},"created_at":{"type":"integer"},"started_at":{"type":["integer","null"]},"completed_at":{"type":["integer","null"]},"consent_attested_at":{"type":["integer","null"],"description":"When the sender confirmed the recipients agreed to be messaged (`consent_attested`), if ever"}},"required":["id","session_id","name","status","body","message_type","total_recipients","sent_count","failed_count","created_at","started_at","completed_at","consent_attested_at"]},"AutoReplyRule":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"name":{"type":"string"},"trigger_type":{"type":"string","enum":["any","keyword","contains","regex"],"description":"`any` replies to every message; `keyword` matches the whole text; `contains` matches a word or phrase inside it; `regex` uses `trigger_text` as a regular expression"},"trigger_text":{"type":["string","null"],"description":"Text or regular expression to compare with; unused by `any`"},"match_case":{"type":"integer","description":"1 = case-sensitive, 0 = not"},"reply_type":{"type":"string","enum":["text","image","document","audio","video","sticker"]},"reply_body":{"type":["string","null"],"description":"Reply text. Supports the personalization tokens `{name}`, `{first_name}`, `{phone}` and `{{first_name|there}}`-style fallbacks"},"reply_r2_key":{"type":["string","null"],"description":"Media key for non-text replies"},"reply_caption":{"type":["string","null"]},"cooldown_ms":{"type":"integer","description":"Minimum time before the same chat gets this rule again"},"priority":{"type":"integer","description":"Higher runs first"},"is_active":{"type":"integer","description":"1 or 0"},"created_at":{"type":"integer"}},"required":["id","session_id","name","trigger_type","trigger_text","match_case","reply_type","reply_body","reply_r2_key","reply_caption","cooldown_ms","priority","is_active","created_at"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"created_at":{"type":"integer"},"disabled_at":{"type":["integer","null"]}},"required":["id","url","events","created_at","disabled_at"]},"InboxChat":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"chat_id":{"type":"string","maxLength":64,"pattern":"^[\\d\\-]+(@(s\\.whatsapp\\.net|g\\.us|newsletter|broadcast|lid))?$"},"chat_name":{"type":["string","null"]},"last_message":{"type":["string","null"]},"last_message_at":{"type":"integer"},"unread_count":{"type":"integer"},"is_group":{"type":"integer","description":"1 = group chat, 0 = 1-on-1"},"agent_id":{"type":["string","null"],"description":"Assigned CS agent, or null for unassigned"},"status":{"type":"string"}},"required":["id","session_id","chat_id","chat_name","last_message","last_message_at","unread_count","is_group","agent_id","status"]},"SendMessageRequest":{"type":"object","properties":{"chat_id":{"type":"string","description":"Recipient: a WhatsApp JID (`447700900123@s.whatsapp.net`, group `…@g.us`) or a phone number. Use the international format `+<country code><number>` (e.g. `+44 7700 900123`); a number in local format (`07700 900123`) is resolved against the business country from the profile, or against `country_code` when given."},"type":{"type":"string","enum":["text","image","document","audio","video","sticker","location","poll","contact"],"description":"What to send; each type needs its own field: `text` → `body`; `image`, `video`, `audio`, `document`, `sticker` → `media_r2_key` or `url`; `location` → `location`; `poll` → `poll_name` + 2–12 `choices`; `contact` → `contact`"},"body":{"type":"string","maxLength":65536,"description":"Text content. Required when type=text"},"caption":{"type":"string","maxLength":1024,"description":"Image/video caption"},"media_r2_key":{"type":"string","maxLength":256,"description":"Key of a file uploaded with `POST /v1/media/upload` (or `upload-url` + `PUT`)"},"url":{"type":"string","maxLength":2048,"format":"uri","description":"Public URL of the media to send instead of `media_r2_key` (downloaded server-side, max 25 MB)"},"filename":{"type":"string","maxLength":255,"description":"File name of a media file sent from `url` (its extension decides the stored file type)"},"location":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"]},"poll_name":{"type":"string","maxLength":200,"description":"Poll question (type=poll)"},"choices":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"maxItems":12,"description":"Poll choices, 2–12 (type=poll)"},"contact":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"phone":{"type":"string","description":"International format"}},"required":["name","phone"],"description":"Contact card sent as a vCard (type=contact)"},"reply_to":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","description":"ULID of message to reply to (quoted reply)"},"connect_only":{"type":"boolean","description":"Default `true`: refuse with 409 `CONFLICT` while the device is not connected. `false`: accept and queue the message until the device reconnects"},"country_code":{"type":"string","pattern":"^\\d{1,4}$","description":"Calling code (1-4 digits, e.g. `44`) used only for local-format numbers; defaults to the business country. `0` never prefixes a code."}},"required":["chat_id","type"]},"SendMessageResponse":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"status":{"type":"string","enum":["queued","sent","failed"]},"idempotent_replay":{"type":"boolean","description":"`true` when an earlier request with the same `Idempotency-Key` was returned instead of sending again"},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"description":"Non-fatal notices, e.g. `RECIPIENT_OPTED_OUT` (the message was still accepted; see `enforce_api` in the consent config)"}},"required":["id","status"]},"AuthSuccess":{"type":"object","properties":{"token":{"type":"string","description":"JWT to use in Authorization: Bearer header"},"refresh_token":{"type":"string","description":"Refresh token (long-lived)"},"role":{"type":"string","enum":["admin","agent"]},"agent_id":{"type":"string"},"agent_name":{"type":"string"}},"required":["token","role"]},"RequestOtpRequest":{"type":"object","properties":{"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$","description":"International format `+<country code><number>`"}},"required":["phone"]},"VerifyOtpRequest":{"type":"object","properties":{"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$"},"code":{"type":"string","minLength":6,"maxLength":6}},"required":["phone","code"]},"RegisterRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"Business or account name"},"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$","description":"Owner phone in international format `+<country code><number>`; also decides the initial business country, timezone and currency"},"lang":{"type":"string","enum":["en","id"],"description":"Language of the OTP / notice messages and the initial dashboard language (default: the request language)"}},"required":["name","phone"]},"BusinessProfile":{"type":"object","properties":{"business_name":{"type":["string","null"]},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 code, e.g. `GB`"},"timezone":{"type":"string","description":"IANA timezone, e.g. `Europe/London`. Drives business hours, the send window and the dates shown in the dashboard"},"locale":{"type":"string","enum":["en","id"],"description":"Language of the messages the platform sends on the business's behalf (OTP, opt-out confirmations, notices)"},"industry":{"type":["string","null"],"enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"],"description":"Selects the onboarding starter pack"},"currency":{"type":"string","enum":["IDR","USD"],"description":"Billing currency (see /v1/billing/options for what can actually be charged)"},"configured":{"type":"boolean","description":"`true` once the owner saved the profile at least once"},"calling_code":{"type":"string","description":"Digits, e.g. `44`. Local-format numbers (`07700 900123`) are resolved with this code"},"suggested_timezones":{"type":"array","items":{"type":"string"},"description":"Timezones of the country, for pickers"},"industries":{"type":"array","items":{"type":"string"}},"locales":{"type":"array","items":{"type":"string"}}},"required":["business_name","country","timezone","locale","industry","currency","configured","calling_code"]},"ConsentConfig":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Listen for opt-out / opt-in keywords in inbound messages (`STOP`, `UNSUBSCRIBE`, `START`, … in several languages)"},"optout_keywords":{"type":"array","items":{"type":"string"}},"optin_keywords":{"type":"array","items":{"type":"string"}},"optout_reply":{"type":["string","null"],"description":"`null` = default confirmation in the tenant language, empty string = send no confirmation"},"optin_reply":{"type":["string","null"]},"enforce_api":{"type":"boolean","description":"Refuse 1:1 sends to opted-out recipients with 409 `RECIPIENT_OPTED_OUT` (default: send and return a warning)"},"require_attestation":{"type":"boolean","description":"Require `consent_attested: true` before a blast starts"}},"required":["enabled","optout_keywords","optin_keywords","optout_reply","optin_reply","enforce_api","require_attestation"]},"Contact":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]},"KnowledgeDocument":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]},"SavedAnswer":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"source":{"type":"string","enum":["manual","unanswered"],"description":"`unanswered`: written from a question the assistant could not answer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","question","answer","source","created_at","updated_at"]},"StoreIntegration":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["shopify","woocommerce"]},"name":{"type":"string","description":"Store name used as {store}"},"session_id":{"type":["string","null"],"description":"The WhatsApp number that sends"},"enabled":{"type":"boolean"},"has_secret":{"type":"boolean","description":"Whether the webhook signing secret is saved (never returned)"},"webhook_url":{"type":"string","description":"Paste into the store's webhook settings"},"templates":{"type":"object","properties":{"order_created":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_paid":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_shipped":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"abandoned_checkout":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]}},"required":["order_created","order_paid","order_shipped","abandoned_checkout"]},"abandoned_delay_min":{"type":"integer"},"abandoned_only_marketing":{"type":"boolean"},"shop_domain":{"type":["string","null"],"description":"As reported by the store's webhooks"},"last_event_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","platform","name","session_id","enabled","has_secret","webhook_url","templates","abandoned_delay_min","abandoned_only_marketing","shop_domain","last_event_at","last_error","created_at"]},"MessageLogSummary":{"type":"object","properties":{"id":{"type":"string","description":"Gateway message id (ULID)"},"direction":{"type":"string","enum":["inbound","outbound"]},"chat_id":{"type":"string","description":"Chat JID"},"message_type":{"type":"string","description":"`text`, `image`, `video`, `document`, `audio`, `sticker`, `location`, `poll`, `contact` or `reaction`"},"body":{"type":["string","null"],"description":"Text content, if any"},"media_r2_key":{"type":["string","null"],"description":"Key of the stored media file; download it with `GET /v1/sessions/{sessionId}/messages/{id}/media`"},"status":{"type":"string","description":"`queued`, `sent`, `delivered`, `read`, `failed`, `cancelled` or `deleted`"},"created_at":{"type":"integer","description":"Creation time (Unix epoch, milliseconds)"}},"required":["id","direction","chat_id","message_type","body","media_r2_key","status","created_at"]},"MessageLogEntry":{"type":"object","properties":{"id":{"type":"string","description":"Gateway message id (ULID)"},"direction":{"type":"string","enum":["inbound","outbound"]},"chat_id":{"type":"string","description":"Chat JID"},"message_type":{"type":"string","description":"`text`, `image`, `video`, `document`, `audio`, `sticker`, `location`, `poll`, `contact` or `reaction`"},"body":{"type":["string","null"],"description":"Text content, if any"},"media_r2_key":{"type":["string","null"],"description":"Key of the stored media file; download it with `GET /v1/sessions/{sessionId}/messages/{id}/media`"},"status":{"type":"string","description":"`queued`, `sent`, `delivered`, `read`, `failed`, `cancelled` or `deleted`"},"created_at":{"type":"integer","description":"Creation time (Unix epoch, milliseconds)"},"wa_message_id":{"type":["string","null"],"description":"WhatsApp message id; null until the message was handed to WhatsApp"},"sent_at":{"type":["integer","null"],"description":"Time the message was handed to WhatsApp (Unix epoch, milliseconds)"},"error":{"type":["string","null"],"description":"Failure reason of a failed message"}},"required":["id","direction","chat_id","message_type","body","media_r2_key","status","created_at","wa_message_id","sent_at","error"]},"InboxChatMetadata":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"}},"required":["name","color"]},"description":"Tags attached to the chat"},"notes":{"type":"array","items":{"type":"string"},"description":"Internal notes, newest first"},"pinned":{"type":"boolean"},"archived":{"type":"boolean"}},"required":["tags","notes","pinned","archived"]},"SessionScheduledMessage":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"chat_id":{"type":"string","description":"Recipient JID as it was submitted"},"message_type":{"type":"string"},"body":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"caption":{"type":["string","null"]},"scheduled_at":{"type":"integer","description":"Time the message is due (Unix epoch, milliseconds)"},"status":{"type":"string","description":"`pending`, `sending`, `sent` or `failed`"},"message_id":{"type":["string","null"],"description":"Gateway message id of the sent message, once sent"},"error":{"type":["string","null"],"description":"Failure reason (for example `opted_out`)"},"created_at":{"type":"integer","description":"Creation time (Unix epoch, milliseconds)"}},"required":["id","session_id","api_key_id","chat_id","message_type","body","media_r2_key","caption","scheduled_at","status","message_id","error","created_at"]},"WaGroupMetadata":{"type":"object","properties":{"id":{"type":"string","description":"Group JID"},"subject":{"type":"string","description":"Group name"},"owner":{"type":"string"},"creation":{"type":"integer","description":"Creation time as reported by WhatsApp (Unix epoch)"},"desc":{"type":"string","description":"Group description"},"size":{"type":"integer"},"restrict":{"type":"boolean","description":"Only admins can change group settings"},"announce":{"type":"boolean","description":"Only admins can write"},"ephemeralDuration":{"type":"integer","description":"Disappearing-messages timer in seconds"},"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"admin":{"type":["string","null"],"description":"`admin`, `superadmin` or null"}},"required":["id"]}}},"required":["id","subject","participants"],"description":"Group metadata as returned by WhatsApp; further fields may be present"},"WaCatalogProduct":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"currency":{"type":"string"},"retailerId":{"type":"string"},"url":{"type":"string"},"isHidden":{"type":"boolean"},"imageUrls":{"type":"object","additionalProperties":{"type":"string"}},"availability":{"type":"string"},"reviewStatus":{"type":"object","additionalProperties":{"type":"string"}}},"required":["id","name"],"description":"Catalog product as returned by WhatsApp; further fields may be present"},"RecurringMessage":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string","description":"Device the message is sent from"},"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"},"description":"Recipient JIDs (phone numbers are normalised to JIDs when saved)"},"message":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}}},"timezone":{"type":"string","description":"IANA timezone the schedule is evaluated in"},"next_run_at":{"type":["integer","null"],"description":"Next planned run, Unix ms"},"last_run_at":{"type":["integer","null"],"description":"Last run, Unix ms"},"is_active":{"type":"boolean"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","name","targets","message","media_r2_key","schedule_type","schedule_config","timezone","next_run_at","last_run_at","is_active","created_at"]},"AdminTenantHealth":{"type":"object","properties":{"tenant_id":{"type":"string"},"name":{"type":"string"},"days":{"type":"integer","description":"Length of the window: 7, 30 or 90"},"timezone":{"type":"string","description":"IANA timezone of the tenant's business profile; day boundaries below are cut at its midnight"},"devices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Session id"},"name":{"type":["string","null"],"description":"WhatsApp display name"},"phone":{"type":["string","null"],"description":"Paired number as digits with country code (no `+`)"},"status":{"type":"string","description":"Current session status"},"connected_at":{"type":["integer","null"],"description":"When the device last connected (Unix epoch, milliseconds)"},"last_active_at":{"type":["integer","null"],"description":"Last activity on the device (Unix epoch, milliseconds)"},"down":{"type":"boolean","description":"`true` when the device was set up and is not connected: status `disconnected` or `failed`. Devices still waiting for a QR scan (`pending`, `qr_ready`) are never down"}},"required":["id","name","phone","status","connected_at","last_active_at","down"]},"description":"Every WhatsApp session of the tenant, oldest first"},"totals":{"type":"object","properties":{"sent":{"type":"integer","description":"Outbound messages with status `sent`, `delivered` or `read`"},"received":{"type":"integer","description":"Inbound messages"},"failed":{"type":"integer","description":"Outbound messages with status `failed`"},"delivered":{"type":"integer","description":"Outbound messages `delivered` or `read`"},"read":{"type":"integer","description":"Outbound messages `read`"},"failure_rate":{"type":["number","null"],"description":"`failed / (sent + failed)` rounded to 3 decimals; `null` when nothing was attempted in the window"}},"required":["sent","received","failed","delivered","read","failure_rate"],"description":"Message volume over the whole window"},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","description":"`YYYY-MM-DD` in the tenant's timezone"},"sent":{"type":"integer"},"received":{"type":"integer"},"failed":{"type":"integer"}},"required":["date","sent","received","failed"]},"description":"One entry per day of the window, oldest first, ending today; days without traffic are zero-filled"}},"required":["tenant_id","name","days","timezone","devices","totals","daily"]},"PaymentWebhookAck":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true`: the event was handled or deliberately ignored (invoice now paid or already paid, unknown order, event type that needs no action). `false`: the payment is not settled yet, see `status`"},"status":{"type":"string","description":"Present when `ok` is `false`: the provider's own payment status while it is still pending, `rejected` (the payment does not match the invoice; final, audited) or `unverified` (Pakasir only; Creem and PayPal answer 503 for this instead)"}},"required":["ok"]}}},"paths":{"/auth/register":{"post":{"description":"Self-service tenant signup. Creates a new tenant with the Free plan; the business profile (country, timezone, language, billing currency) is inferred from the phone number. Phone number must be unique across all tenants.","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"Business or account name"},"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$","description":"Owner phone in international format `+<country code><number>`; also decides the initial business country, timezone and currency"},"lang":{"type":"string","enum":["en","id"],"description":"Language of the OTP / notice messages and the initial dashboard language (default: the request language)"}},"required":["name","phone"]}}}},"responses":{"200":{"description":"Tenant created and a one-time code sent over WhatsApp. Sign in with `POST /auth/verify-otp`.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Name or number missing or invalid","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"Phone already registered — sign in instead","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"The code could not be delivered over WhatsApp","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"Registration is temporarily unavailable (the sender device is offline)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Sign up a new business"}},"/auth/request-otp":{"post":{"description":"Request a one-time password (OTP) for the given phone. Max 5 requests per 15 min per phone.","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$","description":"International format `+<country code><number>`"}},"required":["phone"]}}}},"responses":{"200":{"description":"OTP sent","content":{"application/json":{"schema":{"type":"object","properties":{"ttl":{"type":"integer"},"via":{"type":"string"}},"required":["ttl","via"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many OTP requests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Request a sign-in code"}},"/auth/verify-otp":{"post":{"description":"Verify the 6-digit OTP. Returns a JWT (2 hour expiry) and a rotating refresh token (30 days).","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","minLength":8,"maxLength":20,"pattern":"^[\\+\\d][\\d\\s\\-()]{7,18}\\d$"},"code":{"type":"string","minLength":6,"maxLength":6}},"required":["phone","code"]}}}},"responses":{"200":{"description":"Authenticated","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT to use in Authorization: Bearer header"},"refresh_token":{"type":"string","description":"Refresh token (long-lived)"},"role":{"type":"string","enum":["admin","agent"]},"agent_id":{"type":"string"},"agent_name":{"type":"string"}},"required":["token","role"]}}}},"401":{"description":"Invalid or expired OTP","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Sign in with a one-time code"}},"/auth/refresh":{"post":{"description":"Exchange a valid refresh token for a fresh JWT. The old refresh token is rotated (revoked). Reuse-detection blocks compromised tokens.","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"refresh_token":{"type":"string","minLength":20}},"required":["refresh_token"]}}}},"responses":{"200":{"description":"Refreshed","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT to use in Authorization: Bearer header"},"refresh_token":{"type":"string","description":"Refresh token (long-lived)"},"role":{"type":"string","enum":["admin","agent"]},"agent_id":{"type":"string"},"agent_name":{"type":"string"}},"required":["token","role"]}}}},"401":{"description":"Token revoked or compromised","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Renew a dashboard login"}},"/v1/sessions":{"get":{"description":"List all WhatsApp devices for the calling tenant.","tags":["sessions"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of sessions","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"ULID identifier"},"status":{"type":"string","enum":["pending","qr_ready","connected","disconnected","failed"],"description":"`pending` (starting), `qr_ready` (waiting for a scan), `connected`, `disconnected` (was working, connection lost) or `failed`"},"phone_number":{"type":["string","null"],"description":"Paired WhatsApp number as digits with country code (no `+`), e.g. `447700900123`"},"wa_name":{"type":["string","null"],"description":"WhatsApp display name, read from the phone when it is linked (no need to type it)"},"wa_about":{"type":["string","null"],"description":"The number's WhatsApp \"About\" text (bio), read after linking and refreshed whenever the profile is fetched. `\"\"` = no About; `null` = not read yet or hidden by the phone's privacy settings"},"phone_display":{"type":["string","null"],"description":"The number in international format with its country code, e.g. `+44 7700 900123`"},"phone_country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country of the number, e.g. `GB` (`null` when unknown)"},"phone_country_code":{"type":["string","null"],"description":"Country calling code with `+`, e.g. `+44`"},"api_key_id":{"type":"string","description":"Owning tenant"},"warmup_until_ms":{"type":["integer","null"],"description":"Pacing until this timestamp (anti-ban)"},"created_at":{"type":"integer"},"connected_at":{"type":["integer","null"]}},"required":["id","status","phone_number","wa_name","api_key_id","warmup_until_ms","created_at","connected_at"]}}},"required":["sessions"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List devices"},"post":{"description":"Create a new WhatsApp device (session). Poll `qr_polling_url` for the QR code and scan it from WhatsApp, or link with a pairing code (`POST /v1/sessions/{id}/pairing-code`). Limited by the plan's device quota.","tags":["sessions"],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]},"qr_polling_url":{"type":"string"}},"required":["id","status","qr_polling_url"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Plan device limit reached","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Create a device"}},"/v1/sessions/{id}":{"get":{"description":"Get a single session by id.","tags":["sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Session detail","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"ULID identifier"},"status":{"type":"string","enum":["pending","qr_ready","connected","disconnected","failed"],"description":"`pending` (starting), `qr_ready` (waiting for a scan), `connected`, `disconnected` (was working, connection lost) or `failed`"},"phone_number":{"type":["string","null"],"description":"Paired WhatsApp number as digits with country code (no `+`), e.g. `447700900123`"},"wa_name":{"type":["string","null"],"description":"WhatsApp display name, read from the phone when it is linked (no need to type it)"},"wa_about":{"type":["string","null"],"description":"The number's WhatsApp \"About\" text (bio), read after linking and refreshed whenever the profile is fetched. `\"\"` = no About; `null` = not read yet or hidden by the phone's privacy settings"},"phone_display":{"type":["string","null"],"description":"The number in international format with its country code, e.g. `+44 7700 900123`"},"phone_country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country of the number, e.g. `GB` (`null` when unknown)"},"phone_country_code":{"type":["string","null"],"description":"Country calling code with `+`, e.g. `+44`"},"api_key_id":{"type":"string","description":"Owning tenant"},"warmup_until_ms":{"type":["integer","null"],"description":"Pacing until this timestamp (anti-ban)"},"created_at":{"type":"integer"},"connected_at":{"type":["integer","null"]}},"required":["id","status","phone_number","wa_name","api_key_id","warmup_until_ms","created_at","connected_at"]}}}},"404":{"description":"Not found or not owned by tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Get a device"},"delete":{"tags":["sessions"],"summary":"Delete a device","description":"Closes the live connection, deletes the stored WhatsApp credentials and removes the session together with its auto-reply rules, blast campaigns, blocklist entries and message history. The inbox and scheduled messages are left in place, and WhatsApp is not asked to unlink the device. This cannot be undone.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages":{"post":{"description":"Send a WhatsApp message via the given session. Supports text, media (image/video/document/audio/sticker), location, poll, and contact. If the recipient has opted out the message is still sent and the response carries a `RECIPIENT_OPTED_OUT` warning, unless the tenant enabled `enforce_api` in the consent config, in which case the call fails with 409 `RECIPIENT_OPTED_OUT`.","tags":["messages"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":200},"description":"Any unique string (an order id, a UUID). Repeating a send with the same key returns the first result with `idempotent_replay: true` instead of sending again. Keys are unique per business and kept with the message (message retention)."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Recipient: a WhatsApp JID (`447700900123@s.whatsapp.net`, group `…@g.us`) or a phone number. Use the international format `+<country code><number>` (e.g. `+44 7700 900123`); a number in local format (`07700 900123`) is resolved against the business country from the profile, or against `country_code` when given."},"type":{"type":"string","enum":["text","image","document","audio","video","sticker","location","poll","contact"],"description":"What to send; each type needs its own field: `text` → `body`; `image`, `video`, `audio`, `document`, `sticker` → `media_r2_key` or `url`; `location` → `location`; `poll` → `poll_name` + 2–12 `choices`; `contact` → `contact`"},"body":{"type":"string","maxLength":65536,"description":"Text content. Required when type=text"},"caption":{"type":"string","maxLength":1024,"description":"Image/video caption"},"media_r2_key":{"type":"string","maxLength":256,"description":"Key of a file uploaded with `POST /v1/media/upload` (or `upload-url` + `PUT`)"},"url":{"type":"string","maxLength":2048,"format":"uri","description":"Public URL of the media to send instead of `media_r2_key` (downloaded server-side, max 25 MB)"},"filename":{"type":"string","maxLength":255,"description":"File name of a media file sent from `url` (its extension decides the stored file type)"},"location":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"]},"poll_name":{"type":"string","maxLength":200,"description":"Poll question (type=poll)"},"choices":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"maxItems":12,"description":"Poll choices, 2–12 (type=poll)"},"contact":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"phone":{"type":"string","description":"International format"}},"required":["name","phone"],"description":"Contact card sent as a vCard (type=contact)"},"reply_to":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","description":"ULID of message to reply to (quoted reply)"},"connect_only":{"type":"boolean","description":"Default `true`: refuse with 409 `CONFLICT` while the device is not connected. `false`: accept and queue the message until the device reconnects"},"country_code":{"type":"string","pattern":"^\\d{1,4}$","description":"Calling code (1-4 digits, e.g. `44`) used only for local-format numbers; defaults to the business country. `0` never prefixes a code."}},"required":["chat_id","type"]}}}},"responses":{"200":{"description":"Message accepted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"status":{"type":"string","enum":["queued","sent","failed"]},"idempotent_replay":{"type":"boolean","description":"`true` when an earlier request with the same `Idempotency-Key` was returned instead of sending again"},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"description":"Non-fatal notices, e.g. `RECIPIENT_OPTED_OUT` (the message was still accepted; see `enforce_api` in the consent config)"}},"required":["id","status"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Session not owned by tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"Session not connected (`CONFLICT`) or recipient opted out in strict mode (`RECIPIENT_OPTED_OUT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Send a message"},"get":{"tags":["messages"],"summary":"List message history","description":"Paged history of the messages of a session, optionally filtered by chat, status, direction or text. Each page is ordered oldest first; to go further back pass `next_cursor` as `cursor`. Error texts are PII-redacted (numbers, JIDs, e-mail addresses) and cut to 160 characters.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Only this chat (exact JID)"},"required":false,"description":"Only this chat (exact JID)","name":"chat_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size. Values outside 1-200 are clamped, not rejected"},"required":false,"description":"Page size. Values outside 1-200 are clamped, not rejected","name":"limit","in":"query"},{"schema":{"type":"number","description":"`created_at` of the oldest message already seen; returns older messages only"},"required":false,"description":"`created_at` of the oldest message already seen; returns older messages only","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["queued","sent","delivered","read","failed","cancelled","deleted","blocked"],"description":"Filter by status; unknown values are ignored"},"required":false,"description":"Filter by status; unknown values are ignored","name":"status","in":"query"},{"schema":{"type":"string","enum":["inbound","outbound"],"description":"Filter by direction; other values are ignored"},"required":false,"description":"Filter by direction; other values are ignored","name":"direction","in":"query"},{"schema":{"type":"string","description":"Substring to find in the message text (first 200 characters are used)"},"required":false,"description":"Substring to find in the message text (first 200 characters are used)","name":"q","in":"query"}],"responses":{"200":{"description":"A page of messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageLogEntry"},"description":"Oldest first"},"has_more":{"type":"boolean","description":"`true` when the page is full, so older messages may exist"},"next_cursor":{"type":["integer","null"],"description":"`created_at` of the oldest message in the page (Unix epoch, milliseconds): pass it as `cursor` for the next page. Null for an empty page"}},"required":["messages","has_more","next_cursor"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox":{"get":{"description":"List chats with last message, unread count, and group flag.","tags":["inbox"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Inbox listing","content":{"application/json":{"schema":{"type":"object","properties":{"inbox":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"chat_id":{"type":"string","maxLength":64,"pattern":"^[\\d\\-]+(@(s\\.whatsapp\\.net|g\\.us|newsletter|broadcast|lid))?$"},"chat_name":{"type":["string","null"]},"last_message":{"type":["string","null"]},"last_message_at":{"type":"integer"},"unread_count":{"type":"integer"},"is_group":{"type":"integer","description":"1 = group chat, 0 = 1-on-1"},"agent_id":{"type":["string","null"],"description":"Assigned CS agent, or null for unassigned"},"status":{"type":"string"}},"required":["id","session_id","chat_id","chat_name","last_message","last_message_at","unread_count","is_group","agent_id","status"]}}},"required":["inbox"]}}}}},"summary":"List the chats of a device"}},"/v1/sessions/{sessionId}/blasts":{"post":{"description":"Create a blast campaign (status `draft`). The audience is the explicit `recipients` plus the contacts matched by `segment_id`, `group_id` and/or `tag` (CRM filters combine with AND), deduplicated, max 50 000. Nothing is sent until `/start`. The server applies anti-ban pacing and per-country warm-up; recipients who opted out are skipped automatically just before sending, and bulk sends are only released inside the tenant's send window (see `send_window` in `/v1/settings/send_window`).","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"description":"Campaign name (internal)"},"message_type":{"type":"string","enum":["text","image","video","document","audio"],"default":"text"},"body":{"type":"string","maxLength":65536,"description":"Message text. Personalization tokens: `{name}`, `{first_name}`, `{phone}`, `{var1}`…`{var5}`, `{attr.<key>}`; add a fallback with double braces: `{{first_name|there}}`, `{{attr.plan|your plan}}`. A token without a value and without a fallback becomes empty."},"media_r2_key":{"type":"string","description":"Media key from /v1/media/upload-url (for image/video/document/audio)"},"caption":{"type":"string","maxLength":1024},"footer":{"type":"string","maxLength":200},"buttons":{"description":"Optional button configuration"},"delay_config":{"type":"object","properties":{"min_ms":{"type":"integer"},"max_ms":{"type":"integer"},"typing_min_ms":{"type":"integer"},"typing_max_ms":{"type":"integer"}},"required":["min_ms","max_ms","typing_min_ms","typing_max_ms"],"description":"Pause between recipients. Default 3 000–10 000 ms with 1 000–3 000 ms of simulated typing."},"simulate_typing":{"type":"boolean","description":"Show \"typing…\" before each message (default true)"},"recipients":{"type":"array","items":{"type":"object","properties":{"chat_id":{"type":"string","description":"Phone number (`+<country code><number>`; local numbers use the business country) or JID"},"name":{"type":"string","maxLength":200},"vars":{"type":"array","items":{"type":"string"},"maxItems":5,"description":"Values for `{var1}`…`{var5}`"},"attrs":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Per-recipient values for `{attr.<key>}`"}},"required":["chat_id"]},"maxItems":50000,"description":"Explicit recipients. Combined (deduplicated) with the CRM audience below."},"segment_id":{"type":"string","description":"Add every contact of this saved segment"},"group_id":{"type":"string","description":"Add every contact of this contact group"},"tag":{"type":"string","description":"Add every contact carrying this tag"},"consent_attested":{"type":"boolean","description":"Confirms the recipients agreed to receive these messages. Required (here or when starting) if the tenant turned on `require_attestation`."}},"required":["name"]}}}},"responses":{"201":{"description":"Draft created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"status":{"type":"string","enum":["draft"]},"total_recipients":{"type":"integer"}},"required":["id","status","total_recipients"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Segment or group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Create a campaign"},"get":{"description":"List the campaigns of a session, newest first.","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"}],"responses":{"200":{"description":"Campaigns","content":{"application/json":{"schema":{"type":"object","properties":{"campaigns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"session_id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","running","paused","completed","failed","cancelled"]},"body":{"type":["string","null"]},"message_type":{"type":"string"},"total_recipients":{"type":"integer"},"sent_count":{"type":"integer"},"failed_count":{"type":"integer"},"created_at":{"type":"integer"},"started_at":{"type":["integer","null"]},"completed_at":{"type":["integer","null"]},"consent_attested_at":{"type":["integer","null"],"description":"When the sender confirmed the recipients agreed to be messaged (`consent_attested`), if ever"}},"required":["id","session_id","name","status","body","message_type","total_recipients","sent_count","failed_count","created_at","started_at","completed_at","consent_attested_at"]}}},"required":["campaigns"]}}}}},"summary":"List the campaigns of a device"}},"/v1/sessions/{sessionId}/blasts/{campaignId}":{"get":{"description":"Campaign detail with per-recipient status and progress counters. `skipped_count` counts recipients removed because they opted out.","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"campaignId","in":"path"}],"responses":{"200":{"description":"Campaign","content":{"application/json":{"schema":{"type":"object","properties":{"campaign":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"session_id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","running","paused","completed","failed","cancelled"]},"body":{"type":["string","null"]},"message_type":{"type":"string"},"total_recipients":{"type":"integer"},"sent_count":{"type":"integer"},"failed_count":{"type":"integer"},"created_at":{"type":"integer"},"started_at":{"type":["integer","null"]},"completed_at":{"type":["integer","null"]},"consent_attested_at":{"type":["integer","null"],"description":"When the sender confirmed the recipients agreed to be messaged (`consent_attested`), if ever"}},"required":["id","session_id","name","status","body","message_type","total_recipients","sent_count","failed_count","created_at","started_at","completed_at","consent_attested_at"]},"recipients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"chat_id":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","description":"`pending`, `processing`, `sent`, `failed`, `skipped` or `cancelled`"},"error":{"type":["string","null"]}},"required":["id","chat_id","name","status","error"]}},"pending_count":{"type":"integer"},"sent_count":{"type":"integer"},"failed_count":{"type":"integer"},"skipped_count":{"type":"integer"}},"required":["campaign","recipients","pending_count","sent_count","failed_count","skipped_count"]}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Get a campaign and its progress"},"delete":{"tags":["blast"],"summary":"Delete a campaign","description":"Deletes the campaign and all its recipient rows, whatever its status. Messages that were already sent stay in the message history.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Campaign id"},"required":true,"description":"Campaign id","name":"campaignId","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Campaign not found (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/blasts/{campaignId}/start":{"post":{"description":"Start or resume a campaign. If the tenant turned on `require_attestation` (consent config) and the campaign was not attested at creation, the body must contain `{\"consent_attested\": true}`, otherwise the call fails with 400 `CONSENT_ATTESTATION_REQUIRED`. Opted-out recipients are removed (status `skipped`) before the first message.","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"campaignId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"consent_attested":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Started","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["running"]}},"required":["ok","status"]}}}},"400":{"description":"`CONSENT_ATTESTATION_REQUIRED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Start or resume a campaign"}},"/v1/sessions/{sessionId}/blasts/{campaignId}/pause":{"post":{"description":"Pause a running campaign (resume with `/start`).","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"campaignId","in":"path"}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string"}},"required":["ok","status"]}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Pause a campaign"}},"/v1/sessions/{sessionId}/blasts/{campaignId}/cancel":{"post":{"description":"Cancel a campaign; recipients that were still pending are cancelled.","tags":["blast"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"campaignId","in":"path"}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string"}},"required":["ok","status"]}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Cancel a campaign"}},"/v1/sessions/{sessionId}/auto-reply":{"get":{"description":"List auto-reply rules for a session.","tags":["auto-reply"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"sessionId","in":"path"}],"responses":{"200":{"description":"List of rules","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"name":{"type":"string"},"trigger_type":{"type":"string","enum":["any","keyword","contains","regex"],"description":"`any` replies to every message; `keyword` matches the whole text; `contains` matches a word or phrase inside it; `regex` uses `trigger_text` as a regular expression"},"trigger_text":{"type":["string","null"],"description":"Text or regular expression to compare with; unused by `any`"},"match_case":{"type":"integer","description":"1 = case-sensitive, 0 = not"},"reply_type":{"type":"string","enum":["text","image","document","audio","video","sticker"]},"reply_body":{"type":["string","null"],"description":"Reply text. Supports the personalization tokens `{name}`, `{first_name}`, `{phone}` and `{{first_name|there}}`-style fallbacks"},"reply_r2_key":{"type":["string","null"],"description":"Media key for non-text replies"},"reply_caption":{"type":["string","null"]},"cooldown_ms":{"type":"integer","description":"Minimum time before the same chat gets this rule again"},"priority":{"type":"integer","description":"Higher runs first"},"is_active":{"type":"integer","description":"1 or 0"},"created_at":{"type":"integer"}},"required":["id","session_id","name","trigger_type","trigger_text","match_case","reply_type","reply_body","reply_r2_key","reply_caption","cooldown_ms","priority","is_active","created_at"]}}},"required":["rules"]}}}}},"summary":"List auto-reply rules"},"post":{"tags":["auto-reply"],"summary":"Create an auto-reply rule","description":"Adds a rule that answers inbound messages of this session. Only the first matching active rule answers, and only chats that are not groups, channels or Status. Booleans accept `true`/`false`, `1`/`0` and the strings `\"true\"`, `\"false\"`, `\"1\"`, `\"0\"`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Rule name"},"trigger_type":{"type":"string","enum":["any","keyword","contains","regex"],"description":"`any`: every inbound message; `keyword`: the whole message equals `trigger_text`; `contains`: the message contains `trigger_text`; `regex`: the message matches the regular expression in `trigger_text` (rejected as `UNSAFE_REGEX` when it could backtrack catastrophically)"},"trigger_text":{"type":["string","null"],"maxLength":500,"description":"Text or regular expression to compare with; unused by `any`"},"match_case":{"anyOf":[{"type":"boolean"},{"type":"number","enum":[0]},{"type":"number","enum":[1]},{"type":"string","enum":["true","false","0","1"]}],"description":"Case-sensitive matching (default false)"},"reply_type":{"type":"string","enum":["text","image","document","audio","video","sticker"],"description":"Default `text`"},"reply_body":{"type":["string","null"],"maxLength":4096,"description":"Reply text; personalization tokens such as `{first_name}` or `{{first_name|there}}` are filled from the CRM"},"reply_r2_key":{"type":["string","null"],"maxLength":256,"description":"Media key from `/v1/media/upload-url` for non-text replies"},"reply_caption":{"type":["string","null"],"maxLength":1024,"description":"Caption of a media reply (tokens are filled like in `reply_body`)"},"cooldown_ms":{"type":"integer","minimum":0,"maximum":2592000000,"description":"Minimum time before this rule answers the same chat again (default 60 000)"},"priority":{"type":"integer","minimum":-10000,"maximum":10000,"description":"Rules are tried from the highest priority down; the first match answers (default 0)"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"number","enum":[0]},{"type":"number","enum":[1]},{"type":"string","enum":["true","false","0","1"]}],"description":"Inactive rules never fire (default true)"}},"required":["name","trigger_type"]}}}},"responses":{"201":{"description":"Rule created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Rule id"}},"required":["id"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`) or an unsafe regular expression (`UNSAFE_REGEX`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/webhooks":{"post":{"description":"Register an outbound webhook. URL must be HTTPS, secret must be ≥32 chars. HMAC-SHA256 signed.","tags":["webhooks"],"security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":512,"format":"uri"},"secret":{"type":"string","minLength":32,"maxLength":256},"events":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["url","secret","events"]}}}},"responses":{"201":{"description":"Webhook registered","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"created_at":{"type":"integer"},"disabled_at":{"type":["integer","null"]}},"required":["id","url","events","created_at","disabled_at"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Create a webhook"},"get":{"description":"List webhooks for the calling tenant.","tags":["webhooks"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"created_at":{"type":"integer"},"disabled_at":{"type":["integer","null"]}},"required":["id","url","events","created_at","disabled_at"]}}},"required":["webhooks"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List webhooks"}},"/v1/events-ticket":{"post":{"description":"A single-use ticket for opening the event stream without putting the bearer token in a URL. Call it with your usual `Authorization` header, then open `GET /v1/events?ticket=…` (the returned `url`) with `EventSource` within 60 seconds.","tags":["realtime"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ticket issued","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"string"},"url":{"type":"string"},"expires_at":{"type":"integer"}},"required":["ticket","url","expires_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get a ticket for the event stream"}},"/v1/events":{"get":{"description":"Server-Sent Events (`text/event-stream`) for the calling business: each event has a name and a JSON `data` line. Names: the webhook events (`message.received`, `message.sent`, `message.failed`, `session.connected`, `session.disconnected`, `session.alert`, `qr.generated`, with the same envelope as webhooks) plus `blast.progress`, `blast.waiting`, `blast.paused`, `blast.completed`, `scheduled.sent` and `recurring.executed`. Authenticate with `?ticket=` from `POST /v1/events-ticket` (preferred) or `?token=<API key or dashboard token>`. Reconnect when the connection drops; events sent while disconnected are not replayed (use webhooks for guaranteed delivery).","tags":["realtime"],"responses":{"200":{"description":"SSE stream (text/event-stream)","content":{"text/event-stream":{"schema":{"type":"string"}}}},"401":{"description":"Ticket/token invalid","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Open the event stream (SSE)"}},"/v1/admin/tenants":{"get":{"description":"Every top-level tenant, newest first (no pagination). Includes the plan, device counts (`sessions_connected`, and `sessions_down` for devices that were set up and are now `disconnected` or `failed`), agents and messages sent this month. For one tenant's details see `GET /v1/admin/tenants/{id}/health`. Super-admin only.","tags":["admin"],"security":[{"adminSecret":[]}],"responses":{"200":{"description":"Tenants list","content":{"application/json":{"schema":{"type":"object","properties":{"tenants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"phone":{"type":["string","null"],"description":"Owner's WhatsApp number, if the tenant signed up with one"},"created_at":{"type":"integer"},"revoked_at":{"type":["integer","null"],"description":"Set when the tenant is suspended"},"plan_id":{"type":"integer"},"plan_name":{"type":["string","null"]},"plan_price":{"type":["integer","null"],"description":"Monthly price in IDR"},"plan_active_until":{"type":["integer","null"]},"sessions":{"type":"integer","description":"Devices"},"sessions_connected":{"type":"integer"},"sessions_down":{"type":"integer"},"agents":{"type":"integer","description":"Support agents"},"msgs_month":{"type":["integer","null"],"description":"Messages sent this calendar month (UTC), null before the first send"}},"required":["id","name","phone","created_at","revoked_at","plan_id","plan_name","plan_price","plan_active_until","sessions","sessions_connected","sessions_down","agents","msgs_month"]}}},"required":["tenants"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Create a tenant","description":"Super-admin only. Creates a top-level tenant with its first API key and a dashboard login entry, and initialises its consent settings; when a phone number is given it also writes the initial business profile derived from that number's country. The clear-text `api_key` and `admin_access_code` appear only in this response. Audited as `tenant.provision`.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Business name. Defaults to `tenant`"},"phone":{"type":"string","description":"WhatsApp number of the tenant admin, international (`+62…`) or Indonesian local format (`08…`, read as country ID). It becomes the dashboard login number. A value that cannot be read as a phone number is ignored: the tenant is still created, with `admin_phone: null`"},"plan_id":{"type":"integer","description":"Plan id (see `GET /v1/admin/plans`). Defaults to `1`, the Free plan"}}}}}},"responses":{"201":{"description":"Tenant created","content":{"application/json":{"schema":{"type":"object","properties":{"tenant_id":{"type":"string","description":"Id of the new tenant (ULID); also the id of its primary API key"},"name":{"type":"string"},"api_key":{"type":"string","description":"The tenant's API key in clear text. Returned only in this response; the gateway stores a hash"},"admin_access_code":{"type":"string","description":"Emergency fallback code for the tenant admin's dashboard login (the normal login is WhatsApp number + OTP). Returned only in this response"},"admin_phone":{"type":["string","null"],"description":"Admin login number as digits with country code (no `+`), or `null` when none was given or it could not be read"},"note":{"type":"string"}},"required":["tenant_id","name","api_key","admin_access_code","admin_phone","note"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/profile":{"get":{"tags":["profile"],"security":[{"bearerAuth":[]}],"description":"The business profile of the calling tenant: where the business operates, its timezone, language, industry and billing currency.","responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"type":"object","properties":{"business_name":{"type":["string","null"]},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 code, e.g. `GB`"},"timezone":{"type":"string","description":"IANA timezone, e.g. `Europe/London`. Drives business hours, the send window and the dates shown in the dashboard"},"locale":{"type":"string","enum":["en","id"],"description":"Language of the messages the platform sends on the business's behalf (OTP, opt-out confirmations, notices)"},"industry":{"type":["string","null"],"enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"],"description":"Selects the onboarding starter pack"},"currency":{"type":"string","enum":["IDR","USD"],"description":"Billing currency (see /v1/billing/options for what can actually be charged)"},"configured":{"type":"boolean","description":"`true` once the owner saved the profile at least once"},"calling_code":{"type":"string","description":"Digits, e.g. `44`. Local-format numbers (`07700 900123`) are resolved with this code"},"suggested_timezones":{"type":"array","items":{"type":"string"},"description":"Timezones of the country, for pickers"},"industries":{"type":"array","items":{"type":"string"}},"locales":{"type":"array","items":{"type":"string"}}},"required":["business_name","country","timezone","locale","industry","currency","configured","calling_code"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the business profile"},"put":{"tags":["profile"],"security":[{"bearerAuth":[]}],"description":"Update the business profile (partial update). Account owner only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"business_name":{"type":["string","null"],"maxLength":100},"country":{"type":"string","minLength":2,"maxLength":2,"description":"Changing the country also resets `timezone` and `currency` unless you send them too"},"timezone":{"type":"string"},"locale":{"type":"string","enum":["en","id"]},"industry":{"type":["string","null"],"enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"]},"currency":{"type":"string","enum":["IDR","USD"]}}}}}},"responses":{"200":{"description":"Updated profile","content":{"application/json":{"schema":{"type":"object","properties":{"business_name":{"type":["string","null"]},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 code, e.g. `GB`"},"timezone":{"type":"string","description":"IANA timezone, e.g. `Europe/London`. Drives business hours, the send window and the dates shown in the dashboard"},"locale":{"type":"string","enum":["en","id"],"description":"Language of the messages the platform sends on the business's behalf (OTP, opt-out confirmations, notices)"},"industry":{"type":["string","null"],"enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"],"description":"Selects the onboarding starter pack"},"currency":{"type":"string","enum":["IDR","USD"],"description":"Billing currency (see /v1/billing/options for what can actually be charged)"},"configured":{"type":"boolean","description":"`true` once the owner saved the profile at least once"},"calling_code":{"type":"string","description":"Digits, e.g. `44`. Local-format numbers (`07700 900123`) are resolved with this code"},"suggested_timezones":{"type":"array","items":{"type":"string"},"description":"Timezones of the country, for pickers"},"industries":{"type":"array","items":{"type":"string"}},"locales":{"type":"array","items":{"type":"string"}}},"required":["business_name","country","timezone","locale","industry","currency","configured","calling_code"]}}}},"400":{"description":"Validation error (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the account owner (`FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Update the business profile"}},"/public/countries":{"get":{"tags":["profile"],"description":"Public, cacheable list of countries with their calling code and default timezone, for country pickers (login, signup, profile). No authentication.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["en","id"],"description":"Language of the names/summaries (default `en`)"},"required":false,"description":"Language of the names/summaries (default `en`)","name":"lang","in":"query"}],"responses":{"200":{"description":"Countries sorted by name","content":{"application/json":{"schema":{"type":"object","properties":{"countries":{"type":"array","items":{"type":"object","properties":{"iso":{"type":"string"},"name":{"type":"string"},"calling_code":{"type":"string"},"timezone":{"type":"string"}},"required":["iso","name","calling_code","timezone"]}}},"required":["countries"]}}}}},"summary":"List countries"}},"/public/industries":{"get":{"tags":["profile"],"description":"Public list of industries that have an onboarding starter pack. No authentication.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["en","id"],"description":"Language of the names/summaries (default `en`)"},"required":false,"description":"Language of the names/summaries (default `en`)","name":"lang","in":"query"}],"responses":{"200":{"description":"Industries","content":{"application/json":{"schema":{"type":"object","properties":{"industries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"summary":{"type":"string"}},"required":["id","name","summary"]}}},"required":["industries"]}}}}},"summary":"List industries"}},"/v1/consent":{"get":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Who agreed to be messaged and who asked to stop, newest change first, with totals.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["opted_in","opted_out"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Phone search"},"required":false,"description":"Phone search","name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"description":"Cursor: the `next_before` of the previous page"},"required":false,"description":"Cursor: the `next_before` of the previous page","name":"before","in":"query"},{"schema":{"type":"string","description":"Cursor tie-breaker: the `next_before_id` of the previous page (send it together with `before`, otherwise records sharing one timestamp can be skipped)"},"required":false,"description":"Cursor tie-breaker: the `next_before_id` of the previous page (send it together with `before`, otherwise records sharing one timestamp can be skipped)","name":"before_id","in":"query"}],"responses":{"200":{"description":"Consent records","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"opted_in":{"type":"integer"},"opted_out":{"type":"integer"}},"required":["opted_in","opted_out"]},"consent":{"type":"array","items":{"type":"object","properties":{"chat_id":{"type":"string"},"phone":{"type":"string","description":"Digits with country code (no `+`)"},"status":{"type":"string","enum":["opted_in","opted_out"]},"source":{"type":"string","enum":["keyword","manual","import","api","erasure"],"description":"How the status was set: the customer replied with a keyword, a person in the dashboard, a contacts import with `opt_in`, an API key, or a privacy erasure"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["chat_id","phone","status","source","note","created_at","updated_at"]}},"next_before":{"type":["integer","null"]},"next_before_id":{"type":["string","null"]}},"required":["summary","consent","next_before","next_before_id"]}}}}},"summary":"List consent records"}},"/v1/consent/config":{"get":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Opt-out keywords, confirmation replies and enforcement settings.","responses":{"200":{"description":"Config","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Listen for opt-out / opt-in keywords in inbound messages (`STOP`, `UNSUBSCRIBE`, `START`, … in several languages)"},"optout_keywords":{"type":"array","items":{"type":"string"}},"optin_keywords":{"type":"array","items":{"type":"string"}},"optout_reply":{"type":["string","null"],"description":"`null` = default confirmation in the tenant language, empty string = send no confirmation"},"optin_reply":{"type":["string","null"]},"enforce_api":{"type":"boolean","description":"Refuse 1:1 sends to opted-out recipients with 409 `RECIPIENT_OPTED_OUT` (default: send and return a warning)"},"require_attestation":{"type":"boolean","description":"Require `consent_attested: true` before a blast starts"}},"required":["enabled","optout_keywords","optin_keywords","optout_reply","optin_reply","enforce_api","require_attestation"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the consent settings"},"put":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Update the consent configuration (partial update). Account owner only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Listen for opt-out / opt-in keywords in inbound messages (`STOP`, `UNSUBSCRIBE`, `START`, … in several languages)"},"optout_keywords":{"type":"array","items":{"type":"string"}},"optin_keywords":{"type":"array","items":{"type":"string"}},"optout_reply":{"type":["string","null"],"description":"`null` = default confirmation in the tenant language, empty string = send no confirmation"},"optin_reply":{"type":["string","null"]},"enforce_api":{"type":"boolean","description":"Refuse 1:1 sends to opted-out recipients with 409 `RECIPIENT_OPTED_OUT` (default: send and return a warning)"},"require_attestation":{"type":"boolean","description":"Require `consent_attested: true` before a blast starts"}}}}}},"responses":{"200":{"description":"Saved config","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Listen for opt-out / opt-in keywords in inbound messages (`STOP`, `UNSUBSCRIBE`, `START`, … in several languages)"},"optout_keywords":{"type":"array","items":{"type":"string"}},"optin_keywords":{"type":"array","items":{"type":"string"}},"optout_reply":{"type":["string","null"],"description":"`null` = default confirmation in the tenant language, empty string = send no confirmation"},"optin_reply":{"type":["string","null"]},"enforce_api":{"type":"boolean","description":"Refuse 1:1 sends to opted-out recipients with 409 `RECIPIENT_OPTED_OUT` (default: send and return a warning)"},"require_attestation":{"type":"boolean","description":"Require `consent_attested: true` before a blast starts"}},"required":["enabled","optout_keywords","optin_keywords","optout_reply","optin_reply","enforce_api","require_attestation"]}}}},"403":{"description":"Not the account owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Update the consent settings"}},"/v1/consent/export":{"get":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Download all consent records as CSV (`phone,status,source,note,updated_at`).","responses":{"200":{"description":"CSV file","content":{"text/csv":{"schema":{"type":"string"}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Export consent records (CSV)"}},"/v1/consent/bulk":{"post":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Set the consent status of up to 10 000 numbers at once (e.g. importing a suppression list).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phones":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":10000},"status":{"type":"string","enum":["opted_in","opted_out"]},"note":{"type":["string","null"],"maxLength":200}},"required":["phones","status"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"changed":{"type":"integer"},"unchanged":{"type":"integer"},"invalid":{"type":"integer"}},"required":["changed","unchanged","invalid"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Set consent for many numbers"}},"/v1/consent/history/{phone}":{"get":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"The append-only consent history of one person (the proof of consent).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"International format `+<country code><number>` (URL-encode the `+`); digits only also work. Local numbers use the business country."},"required":true,"description":"International format `+<country code><number>` (URL-encode the `+`); digits only also work. Local numbers use the business country.","name":"phone","in":"path"}],"responses":{"200":{"description":"History","content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"current":{"type":["object","null"],"properties":{"chat_id":{"type":"string"},"phone":{"type":"string","description":"Digits with country code (no `+`)"},"status":{"type":"string","enum":["opted_in","opted_out"]},"source":{"type":"string","enum":["keyword","manual","import","api","erasure"],"description":"How the status was set: the customer replied with a keyword, a person in the dashboard, a contacts import with `opt_in`, an API key, or a privacy erasure"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["chat_id","phone","status","source","note","created_at","updated_at"]},"history":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["opted_in","opted_out"]},"source":{"type":"string"},"note":{"type":["string","null"]},"at":{"type":"integer"}},"required":["status","source","note","at"]}}},"required":["phone","current","history"]}}}},"400":{"description":"`INVALID_NUMBER`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Get the consent history of a number"}},"/v1/consent/{phone}":{"put":{"tags":["consent"],"security":[{"bearerAuth":[]}],"description":"Opt one person in or out.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"International format `+<country code><number>` (URL-encode the `+`); digits only also work. Local numbers use the business country."},"required":true,"description":"International format `+<country code><number>` (URL-encode the `+`); digits only also work. Local numbers use the business country.","name":"phone","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["opted_in","opted_out"]},"note":{"type":["string","null"],"maxLength":200}},"required":["status"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"changed":{"type":"boolean"},"consent":{"type":"object","properties":{"chat_id":{"type":"string"},"phone":{"type":"string","description":"Digits with country code (no `+`)"},"status":{"type":"string","enum":["opted_in","opted_out"]},"source":{"type":"string","enum":["keyword","manual","import","api","erasure"],"description":"How the status was set: the customer replied with a keyword, a person in the dashboard, a contacts import with `opt_in`, an API key, or a privacy erasure"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["chat_id","phone","status","source","note","created_at","updated_at"]}},"required":["ok","changed","consent"]}}}},"400":{"description":"`INVALID_NUMBER`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Opt a number in or out"}},"/v1/privacy/contact":{"get":{"tags":["privacy"],"security":[{"bearerAuth":[]}],"description":"Export everything held about one person (data-subject access request): consent and its history, contacts, messages (up to 5 000), inbox, assignments, blast participation, scheduled and recurring messages, satisfaction answers. Account owner or API key only.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"International format `+<country code><number>`"},"required":true,"description":"International format `+<country code><number>`","name":"phone","in":"query"}],"responses":{"200":{"description":"Everything held for that number","content":{"application/json":{"schema":{"type":"object","properties":{"exported_at":{"type":"string"},"phone":{"type":"string"},"consent":{},"consent_history":{"type":"array","items":{}},"contacts":{"type":"array","items":{}},"messages":{"type":"array","items":{}},"messages_truncated":{"type":"boolean"},"inbox":{"type":"array","items":{}},"chat_metadata":{"type":"array","items":{}},"assignments":{"type":"array","items":{}},"blast_participation":{"type":"array","items":{}},"scheduled_messages":{"type":"array","items":{}},"recurring_messages":{"type":"array","items":{}},"csat_responses":{"type":"array","items":{}}},"required":["exported_at","phone","consent_history","contacts","messages","messages_truncated","inbox","chat_metadata","assignments","blast_participation","scheduled_messages","recurring_messages","csat_responses"]}}}},"400":{"description":"`INVALID_NUMBER`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Export the data of one person"},"delete":{"tags":["privacy"],"security":[{"bearerAuth":[]}],"description":"Erase everything held about one person (right to erasure). Irreversible. `confirm` must repeat the phone number, otherwise the call fails with 400 `CONFIRMATION_REQUIRED`. By default an opt-out record is kept so the person is not messaged again (`keep_suppression: false` removes it too). Account owner or API key only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"confirm":{"type":"string","description":"The same phone number again"},"keep_suppression":{"type":"boolean","description":"Default `true`"}},"required":["phone","confirm"]}}}},"responses":{"200":{"description":"Erased","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"object","additionalProperties":{"type":"integer"},"description":"Rows removed per data type"},"suppression_kept":{"type":"boolean"},"failed":{"type":"array","items":{"type":"string"},"description":"Data types that could not be erased; empty when the erasure is complete"}},"required":["ok","deleted","suppression_kept","failed"]}}}},"400":{"description":"`INVALID_NUMBER` or `CONFIRMATION_REQUIRED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`ERASURE_INCOMPLETE`: some records could not be deleted (`failed` lists them); repeat the request or contact support","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"},"code":{"type":"string"},"deleted":{"type":"object","additionalProperties":{"type":"integer"}},"suppression_kept":{"type":"boolean"},"failed":{"type":"array","items":{"type":"string"}}},"required":["ok","error","code","deleted","suppression_kept","failed"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Erase the data of one person"}},"/v1/privacy/retention":{"get":{"tags":["privacy"],"security":[{"bearerAuth":[]}],"description":"How long message content is kept.","responses":{"200":{"description":"Retention","content":{"application/json":{"schema":{"type":"object","properties":{"message_days":{"type":["integer","null"],"description":"Your own setting; `null` = platform default"},"effective_days":{"type":"integer"},"default_days":{"type":"integer"},"min_days":{"type":"integer"},"max_days":{"type":"integer"}},"required":["message_days","effective_days","default_days","min_days","max_days"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the message retention"},"put":{"tags":["privacy"],"security":[{"bearerAuth":[]}],"description":"Set the message retention period in days (between `min_days` and `max_days`), or `null` for the platform default. Account owner only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message_days":{"type":["integer","null"]}},"required":["message_days"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"message_days":{"type":["integer","null"]},"effective_days":{"type":"integer"}},"required":["message_days","effective_days"]}}}},"400":{"description":"Out of range (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Set the message retention"}},"/v1/crm/fields":{"get":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Custom contact attributes defined by the business (max 30).","responses":{"200":{"description":"Fields","content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,29}$","description":"Lowercase letters, digits, `_`; starts with a letter; max 30"},"label":{"type":"string","maxLength":60},"type":{"type":"string","enum":["text","number","date","select"]},"options":{"type":"array","items":{"type":"string","maxLength":60},"maxItems":50,"description":"Required for `select`"}},"required":["key","label","type"]}}},"required":["fields"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List custom contact fields"},"put":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Replace the whole list of custom attribute definitions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,29}$","description":"Lowercase letters, digits, `_`; starts with a letter; max 30"},"label":{"type":"string","maxLength":60},"type":{"type":"string","enum":["text","number","date","select"]},"options":{"type":"array","items":{"type":"string","maxLength":60},"maxItems":50,"description":"Required for `select`"}},"required":["key","label","type"]},"maxItems":30}},"required":["fields"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,29}$","description":"Lowercase letters, digits, `_`; starts with a letter; max 30"},"label":{"type":"string","maxLength":60},"type":{"type":"string","enum":["text","number","date","select"]},"options":{"type":"array","items":{"type":"string","maxLength":60},"maxItems":50,"description":"Required for `select`"}},"required":["key","label","type"]}}},"required":["fields"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Replace the custom contact fields"}},"/v1/crm/tags":{"get":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"All tags in use with the number of contacts carrying each.","responses":{"200":{"description":"Tags","content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string"},"count":{"type":"integer"}},"required":["tag","count"]}}},"required":["tags"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List contact tags"}},"/v1/crm/contacts/tags":{"post":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Add and/or remove tags on many contacts, selected by `ids` or by a saved `segment_id`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"maxItems":50000},"segment_id":{"type":"string"},"add":{"type":"array","items":{"type":"string","maxLength":80},"maxItems":20},"remove":{"type":"array","items":{"type":"string","maxLength":80},"maxItems":20}}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer"},"skipped":{"type":"integer"}},"required":["updated","skipped"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Tag or untag many contacts"}},"/v1/crm/segments":{"get":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Saved segments (dynamic audiences) with their current size.","responses":{"200":{"description":"Segments","content":{"application/json":{"schema":{"type":"object","properties":{"segments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]},"count":{"type":["integer","null"],"description":"Number of matching contacts (list only; cached for about a minute). `null` while it has not been counted yet"},"count_pending":{"type":"boolean","description":"`true` when `count` is `null` because the count is still being computed: only a few segments are counted per call, ask again shortly"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","filter","created_at","updated_at"]}}},"required":["segments"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List segments"},"post":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Create a segment (max 200). Use its id as `segment_id` when creating a blast or listing contacts.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]}},"required":["name","filter"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]},"count":{"type":["integer","null"],"description":"Number of matching contacts (list only; cached for about a minute). `null` while it has not been counted yet"},"count_pending":{"type":"boolean","description":"`true` when `count` is `null` because the count is still being computed: only a few segments are counted per call, ask again shortly"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","filter","created_at","updated_at"]}}}},"400":{"description":"Invalid filter","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"`LIMIT_REACHED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Create a segment"}},"/v1/crm/segments/preview":{"post":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"How many contacts a filter matches, plus a sample (max 50), without saving it.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]},"limit":{"type":"integer","minimum":1,"maximum":50}},"required":["filter"]}}}},"responses":{"200":{"description":"Preview","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"sample":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}}},"required":["count","sample"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Preview a segment filter"}},"/v1/crm/segments/{id}":{"put":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Rename a segment and/or change its filter.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filter":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"description":"`all` = AND, `any` = OR"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"`name`, `phone`, `group`, `tag`, `consent`, `created` or `attr.<key>`"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","starts_with","empty","not_empty","gt","gte","lt","lte","has","not_has","has_any","before","after","within_days"],"description":"Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; `tag` takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"required":["field","op"]},"maxItems":20}},"required":["match","rules"]},"count":{"type":["integer","null"],"description":"Number of matching contacts (list only; cached for about a minute). `null` while it has not been counted yet"},"count_pending":{"type":"boolean","description":"`true` when `count` is `null` because the count is still being computed: only a few segments are counted per call, ask again shortly"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","filter","created_at","updated_at"]}}}},"404":{"description":"Segment not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Update a segment"},"delete":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Delete a segment (its contacts are not touched).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Segment not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Delete a segment"}},"/v1/crm/segments/{id}/contacts":{"get":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"The contacts currently matching a segment.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"]},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"]},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Contacts","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}}},"required":["total","contacts"]}}}},"404":{"description":"Segment not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"List the contacts of a segment"}},"/v1/crm/contacts/by-phone/{phone}":{"get":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"The contact card of one number (used by the inbox sidebar): the saved contact, if any, and its consent state.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"International format `+<country code><number>` (URL-encode the `+`)"},"required":true,"description":"International format `+<country code><number>` (URL-encode the `+`)","name":"phone","in":"path"}],"responses":{"200":{"description":"Contact card","content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"contact":{"type":["object","null"],"properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]},"consent":{"type":["object","null"],"properties":{"status":{"type":"string","enum":["opted_in","opted_out"]},"source":{"type":"string"},"updated_at":{"type":"integer"}},"required":["status","source","updated_at"]}},"required":["phone","contact","consent"]}}}},"400":{"description":"`INVALID_NUMBER`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Get the contact card of a number"},"put":{"tags":["crm"],"security":[{"bearerAuth":[]}],"description":"Create or update the contact for a number (upsert). `tags` replaces, `add_tags` / `remove_tags` modify. For `attrs`, an empty string removes a key; values are strings of up to 500 characters and typed attributes are validated against the field definitions.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"International format `+<country code><number>` (URL-encode the `+`)"},"required":true,"description":"International format `+<country code><number>` (URL-encode the `+`)","name":"phone","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"maxLength":200},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string","maxLength":80}},"add_tags":{"type":"array","items":{"type":"string","maxLength":80}},"remove_tags":{"type":"array","items":{"type":"string","maxLength":80}},"attrs":{"type":"object","additionalProperties":{"type":"string","maxLength":500}}}}}}},"responses":{"200":{"description":"Existing contact updated","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"boolean","enum":[false]},"contact":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}},"required":["created","contact"]}}}},"201":{"description":"Contact created","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"boolean","enum":[true]},"contact":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}},"required":["created","contact"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Create or update a contact by number"}},"/v1/contacts":{"get":{"tags":["contacts"],"security":[{"bearerAuth":[]}],"description":"List contacts (max 10 000 per call; page with `offset`).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Saved segment id"},"required":false,"description":"Saved segment id","name":"segment","in":"query"},{"schema":{"type":"string"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string"},"required":false,"name":"tag","in":"query"},{"schema":{"type":"string"},"required":false,"name":"group_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":10000},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Contacts","content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}},"total":{"type":"integer"}},"required":["contacts","total"]}}}}},"summary":"List contacts"},"post":{"tags":["contacts"],"security":[{"bearerAuth":[]}],"description":"Import up to 10 000 contacts. A number that already exists is merged into the existing contact (tags are unioned) instead of duplicated. Rows that cannot be stored are returned in `rejected` (first 50).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"phone":{"type":"string","description":"International format `+<country code><number>`; local-format numbers use the business country"},"name":{"type":"string","maxLength":200},"vars":{"type":"array","items":{"type":"string","maxLength":500},"maxItems":5,"description":"Legacy columns `{var1}`…`{var5}`"},"group_id":{"type":"string"},"tags":{"type":"array","items":{"type":"string","maxLength":80},"description":"Merged (union) with the tags the contact already has"},"attrs":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Custom attributes, keys as defined in /v1/crm/fields"},"opt_in":{"type":"boolean","description":"`true` records consent with source `import` (you state this person agreed to be messaged). It never overrides an existing opt-out."}},"required":["phone"]},"maxItems":10000}},"required":["contacts"]}}}},"responses":{"201":{"description":"Imported","content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"count":{"type":"integer"},"created":{"type":"integer"},"updated":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"error":{"type":"string"}},"required":["index","error"]}}},"required":["ids","count","created","updated"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Import contacts"}},"/v1/contacts/{id}":{"patch":{"tags":["contacts"],"security":[{"bearerAuth":[]}],"description":"Update one contact. `attrs` is merged: an empty string removes a key.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"name":{"type":["string","null"]},"vars":{"type":"array","items":{"type":["string","null"]},"maxItems":5},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"contact":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"var1":{"type":["string","null"]},"var2":{"type":["string","null"]},"var3":{"type":["string","null"]},"var4":{"type":["string","null"]},"var5":{"type":["string","null"]},"group_id":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"attrs":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom attributes (`attr.<key>` in segments and message tokens)"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","phone","name","var1","var2","var3","var4","var5","group_id","tags","attrs","created_at","updated_at"]}},"required":["ok","contact"]}}}},"404":{"description":"Contact or group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Update a contact"},"delete":{"tags":["contacts"],"security":[{"bearerAuth":[]}],"description":"Delete one contact.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}},"summary":"Delete a contact"}},"/v1/contacts/bulk":{"post":{"tags":["contacts"],"security":[{"bearerAuth":[]}],"description":"Delete or move many contacts at once.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":10000},"action":{"type":"string","enum":["delete","move"]},"group_id":{"type":["string","null"],"description":"Target group for `move` (`null` = no group)"}},"required":["ids","action"]}}}},"responses":{"200":{"description":"Result: `{deleted}` or `{moved}`","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"},"moved":{"type":"integer"}}}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Delete or move many contacts"}},"/v1/settings/{key}":{"get":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Read a tenant setting. Keys: `send_window`, `business_hours`, `welcome_message`, `csat_config`, `chatbot_flows`, `canned_responses`, `typing_delay`. `value` is `null` when never set. **`send_window`**: blast (bulk) sends are only released inside this daily window (default 08:00–20:00 in the business timezone, or in each recipient's own timezone) and wait for the next opening otherwise; 1:1 replies, scheduled and recurring messages are unaffected.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Setting","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"value":{}},"required":["key"]}}}},"404":{"description":"Unknown key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Read a setting"},"put":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Write a tenant setting. For `send_window` the body is `{enabled, start, end, basis}`; any other key takes its own JSON structure.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"key","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"start":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"`HH:MM`, local time"},"end":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"`HH:MM`, local time"},"basis":{"type":"string","enum":["business","recipient"],"description":"`business` = the profile timezone; `recipient` = the timezone of each recipient's country (derived from the number)"}},"required":["enabled","start","end","basis"],"description":"Shown for `send_window`"}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Write a setting"},"delete":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Reset a tenant setting to its default.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Reset","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}},"summary":"Reset a setting"}},"/v1/onboarding/packs":{"get":{"tags":["onboarding"],"security":[{"bearerAuth":[]}],"description":"Industry starter packs (welcome message, business hours, chatbot flows, message templates, CRM fields, segments, satisfaction survey) with what each contains.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["en","id"],"description":"Language of the names/summaries (default `en`)"},"required":false,"description":"Language of the names/summaries (default `en`)","name":"lang","in":"query"}],"responses":{"200":{"description":"Packs","content":{"application/json":{"schema":{"type":"object","properties":{"packs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"summary":{"type":"string"},"counts":{"type":"object","properties":{"templates":{"type":"integer"},"fields":{"type":"integer"},"segments":{"type":"integer"}},"required":["templates","fields","segments"]}},"required":["id","name","summary","counts"]}}},"required":["packs"]}}}}},"summary":"List industry starter packs"}},"/v1/onboarding/packs/{industry}":{"get":{"tags":["onboarding"],"security":[{"bearerAuth":[]}],"description":"Full preview of one pack, in the requested language (default: the business language).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"]},"required":true,"name":"industry","in":"path"},{"schema":{"type":"string","enum":["en","id"],"description":"Language of the names/summaries (default `en`)"},"required":false,"description":"Language of the names/summaries (default `en`)","name":"lang","in":"query"}],"responses":{"200":{"description":"Pack","content":{"application/json":{"schema":{"type":"object","properties":{"pack":{"type":"object","additionalProperties":{}}},"required":["pack"]}}}},"404":{"description":"Unknown industry","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Preview a starter pack"}},"/v1/onboarding/apply":{"post":{"tags":["onboarding"],"security":[{"bearerAuth":[]}],"description":"Write a pack into the tenant's own configuration. `fill` (default) only adds what is missing; `replace` overwrites the pack's own items (same setting, template name, field key or segment name).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"industry":{"type":"string","enum":["ecommerce","retail","restaurant","healthcare","beauty","fitness","education","real_estate","automotive","travel","hospitality","logistics","finance","insurance","saas","professional_services","events","nonprofit","government","other"]},"parts":{"type":"array","items":{"type":"string","enum":["welcome","hours","csat","flows","templates","fields","segments"]},"description":"Default: all parts"},"mode":{"type":"string","enum":["fill","replace"]},"lang":{"type":"string","enum":["en","id"],"description":"Language of the created texts (default: the business language)"},"set_profile_industry":{"type":"boolean","description":"Also store the industry on the business profile"}},"required":["industry"]}}}},"responses":{"200":{"description":"What was created, updated or skipped per part","content":{"application/json":{"schema":{"type":"object","properties":{"industry":{"type":"string"},"lang":{"type":"string"},"mode":{"type":"string"},"parts":{"type":"object","additionalProperties":{"type":"object","properties":{"created":{"type":"integer"},"updated":{"type":"integer"},"skipped":{"type":"integer"}},"required":["created","updated","skipped"]}}},"required":["industry","lang","mode","parts"]}}}},"404":{"description":"Unknown industry","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Apply a starter pack"}},"/v1/onboarding/status":{"get":{"tags":["onboarding"],"security":[{"bearerAuth":[]}],"description":"The setup checklist with live completion.","responses":{"200":{"description":"Checklist","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["profile","industry","connect","contacts","automation","first_message","consent","plan"]},"done":{"type":"boolean"},"optional":{"type":"boolean"}},"required":["id","done"]}},"done":{"type":"integer"},"total":{"type":"integer"},"dismissed":{"type":"boolean"},"industry":{"type":["string","null"]}},"required":["items","done","total","dismissed","industry"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the setup checklist"}},"/v1/onboarding/state":{"put":{"tags":["onboarding"],"security":[{"bearerAuth":[]}],"description":"Dismiss the checklist or tick the manual \"consent\" step.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"dismissed":{"type":"boolean"},"checked":{"type":"object","properties":{"consent":{"type":"boolean"}}}}}}}},"responses":{"200":{"description":"New state","content":{"application/json":{"schema":{"type":"object","properties":{"dismissed":{"type":"boolean"},"checked":{"type":"object","properties":{"consent":{"type":"boolean"}}}},"required":["dismissed","checked"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Update the setup checklist"}},"/v1/billing/plans":{"get":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Plans with their price in every currency they are sold in. All amounts are **integers in minor units** (cents; IDR has no minor unit), e.g. `{\"IDR\": 99000, \"USD\": 900}`.","responses":{"200":{"description":"Plans","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"price_monthly":{"type":"integer","description":"Price in IDR"},"max_sessions":{"type":"integer"},"max_messages_per_month":{"type":"integer","description":"0 = unlimited"},"send_per_minute":{"type":"integer","description":"Requests per minute the plan allows on `POST …messages…` (see `GET /v1/usage`)"},"ai_replies_per_month":{"type":"integer","description":"AI auto-replies included per month (0 = none)"},"prices":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","name","price_monthly","max_sessions","max_messages_per_month","send_per_minute","ai_replies_per_month","prices"]}},"annual_months_charged":{"type":"integer","description":"Months charged for an annual period (12 months delivered)"}},"required":["plans","annual_months_charged"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List plans and prices"}},"/v1/billing/options":{"get":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"How this tenant can pay: only payment methods that are set up are listed. `default_provider` is Pakasir (QRIS, bank, e-wallet; IDR) for Indonesian tenants and the cheapest international method otherwise. `currency` is what each provider would charge for the tenant's plan; it is `null` when the plan has no price in a currency that provider accepts.","responses":{"200":{"description":"Payment options","content":{"application/json":{"schema":{"type":"object","properties":{"default_provider":{"type":["string","null"],"enum":["pakasir","creem","paypal"]},"tenant_currency":{"type":"string","enum":["IDR","USD"]},"providers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["pakasir","creem","paypal"]},"label":{"type":"string"},"currencies":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"}},"currency":{"type":["string","null"],"pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"}},"required":["id","label","currencies","currency"]}}},"required":["default_provider","tenant_currency","providers"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List payment methods"}},"/v1/billing/quote":{"post":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Price a checkout without creating anything: the same body as `POST /v1/billing/checkout`. Use it to show the annual saving, check a coupon (400 `COUPON_INVALID` with `reason`: `not_found`, `inactive`, `not_started`, `expired`, `used_up`, `already_used`, `wrong_plan`, `wrong_period`, `wrong_currency`) and show the upgrade credit before paying. `provider` is the method that would be used (null when none is set up).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"integer","description":"Plan to buy; omit to renew the current plan"},"provider":{"type":"string","enum":["pakasir","creem","paypal"]},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"billing_period":{"type":"string","enum":["monthly","annual"],"description":"Default `monthly`"},"coupon_code":{"type":"string","description":"Case-insensitive"}}}}}},"responses":{"200":{"description":"Quote","content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"integer"},"plan_name":{"type":"string"},"billing_period":{"type":"string","enum":["monthly","annual"]},"months":{"type":"integer","description":"Months charged (12 months are delivered for an annual period)"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"change":{"type":"string","enum":["new","renew","upgrade","downgrade"],"description":"`upgrade` starts now with a credit; `downgrade` starts when the current period ends; `renew` stacks after it"},"list_amount":{"type":"integer","description":"Plan price × months charged"},"monthly_equivalent":{"type":"integer","description":"What the same months would cost month by month"},"discount_amount":{"type":"integer"},"coupon_code":{"type":["string","null"]},"proration_credit":{"type":"integer","description":"Unused part of what was paid for the current plan (upgrades only)"},"overage":{"type":"integer"},"amount":{"type":"integer","description":"What will be charged, minor units"},"starts_at":{"type":"integer"},"ends_at":{"type":"integer"},"provider":{"type":["string","null"],"enum":["pakasir","creem","paypal"]}},"required":["plan_id","plan_name","billing_period","months","currency","change","list_amount","monthly_equivalent","discount_amount","coupon_code","proration_credit","overage","amount","starts_at","ends_at","provider"]}}}},"400":{"description":"`VALIDATION_FAILED`, `COUPON_INVALID`, `CURRENCY_NOT_SUPPORTED`, or nothing to pay","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Price a checkout"}},"/v1/billing/checkout":{"post":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Create an invoice and a hosted payment page. Redirect the browser to `pay_url`; the provider sends the buyer back to `/billing?invoice=<order_id>` (or `?cancelled=<order_id>`). Omit `plan_id` to pay the current plan plus any overage for the month; send `plan_id` to buy or switch plans. `billing_period: \"annual\"` buys 12 months for the price of 10 (set by the operator). A `coupon_code` takes a percent or a fixed amount off the plan price (never off overage). Switching to a pricier plan starts it now and credits the unused part of what was paid for the current plan; a cheaper plan starts when the current period ends. Price the same body first with `POST /v1/billing/quote`. `provider` and `currency` default to the best match for the tenant. The plan is activated only after the provider confirms the payment (webhook or `/invoices/{order_id}/confirm`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"integer","description":"Plan to buy; omit to renew the current plan"},"provider":{"type":"string","enum":["pakasir","creem","paypal"]},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"billing_period":{"type":"string","enum":["monthly","annual"],"description":"Default `monthly`"},"coupon_code":{"type":"string","description":"Case-insensitive"}}}}}},"responses":{"200":{"description":"Payment created. With `amount` 0 (a 100 % coupon, or an upgrade credit that covers everything) nothing is collected: `status` is `paid`, `pay_url` is null and the plan is already active","content":{"application/json":{"schema":{"type":"object","properties":{"pay_url":{"type":["string","null"],"format":"uri"},"order_id":{"type":"string"},"amount":{"type":"integer","description":"Minor units of `currency`"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"provider":{"type":"string","enum":["pakasir","creem","paypal","none"]},"status":{"type":"string","enum":["pending","paid"]},"overage":{"type":"integer","description":"Part of `amount` that is usage beyond the quota (minor units)"},"quote":{"type":"object","properties":{"plan_id":{"type":"integer"},"plan_name":{"type":"string"},"billing_period":{"type":"string","enum":["monthly","annual"]},"months":{"type":"integer","description":"Months charged (12 months are delivered for an annual period)"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"change":{"type":"string","enum":["new","renew","upgrade","downgrade"],"description":"`upgrade` starts now with a credit; `downgrade` starts when the current period ends; `renew` stacks after it"},"list_amount":{"type":"integer","description":"Plan price × months charged"},"monthly_equivalent":{"type":"integer","description":"What the same months would cost month by month"},"discount_amount":{"type":"integer"},"coupon_code":{"type":["string","null"]},"proration_credit":{"type":"integer","description":"Unused part of what was paid for the current plan (upgrades only)"},"overage":{"type":"integer"},"amount":{"type":"integer","description":"What will be charged, minor units"},"starts_at":{"type":"integer"},"ends_at":{"type":"integer"}},"required":["plan_id","plan_name","billing_period","months","currency","change","list_amount","monthly_equivalent","discount_amount","coupon_code","proration_credit","overage","amount","starts_at","ends_at"]}},"required":["pay_url","order_id","amount","currency","provider","status","overage","quote"]}}}},"400":{"description":"`VALIDATION_FAILED`; `COUPON_INVALID` (with `reason`); `CURRENCY_NOT_SUPPORTED` when the plan has no price in a currency that provider accepts; or nothing to pay (free plan)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"The payment service is unavailable; nothing was stored, retry shortly","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"No payment provider is configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Create an invoice and payment page"}},"/v1/billing/invoices":{"get":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"The 24 most recent invoices of the tenant.","responses":{"200":{"description":"Invoices","content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"order_id":{"type":"string"},"period":{"type":"string","description":"`YYYY-MM`"},"plan_id":{"type":["integer","null"]},"amount":{"type":"integer","description":"Minor units of `currency`"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"provider":{"type":"string","enum":["pakasir","creem","paypal","none"],"description":"`none` = nothing was collected (100 % coupon or credit)"},"billing_period":{"type":"string","enum":["monthly","annual"]},"months":{"type":"integer"},"list_amount":{"type":["integer","null"]},"discount_amount":{"type":"integer"},"coupon_code":{"type":["string","null"]},"proration_credit":{"type":"integer"},"overage_amount":{"type":"integer"},"plan_change":{"type":["string","null"],"enum":["new","renew","upgrade","downgrade"]},"status":{"type":"string","description":"`pending`, `paid`, `expired`, `cancelled` or `failed`"},"created_at":{"type":"integer"},"paid_at":{"type":["integer","null"]}},"required":["order_id","period","plan_id","amount","currency","provider","billing_period","months","list_amount","discount_amount","coupon_code","proration_credit","overage_amount","plan_change","status","created_at","paid_at"]}}},"required":["invoices"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List invoices"}},"/v1/billing/invoices/{order_id}/confirm":{"post":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Ask the payment provider about one invoice (call it when the buyer returns from checkout, or when the webhook is late). Rate-limited to one real check per 4 seconds per invoice; faster calls answer `pending` with `retry_after_s`. A `paid` result activates the plan.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"order_id","in":"path"}],"responses":{"200":{"description":"Current state","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["paid","pending","rejected","unverified"],"description":"`rejected` = the payment does not match the invoice (amount, order, test mode) and was not activated; `unverified` = it cannot be verified automatically, contact support"},"retry_after_s":{"type":"number"},"provider_status":{"type":"string"},"note":{"type":"string"}},"required":["status"]}}}},"404":{"description":"Invoice not found (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Check the payment of an invoice"}},"/v1/admin/settings/payments":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Configuration status of every payment provider. Secrets are write-only: they appear as `has_<field>` booleans, never as values. Pakasir is edited with `PUT /v1/admin/settings/pakasir`.","responses":{"200":{"description":"Providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"object","properties":{"pakasir":{"type":"object","properties":{"configured":{"type":"boolean"},"webhook_path":{"type":"string","description":"Path to register at the provider, appended to the API base URL"}},"required":["configured","webhook_path"]},"creem":{"type":"object","properties":{"configured":{"type":"boolean"},"webhook_path":{"type":"string","description":"Path to register at the provider, appended to the API base URL"}},"required":["configured","webhook_path"]},"paypal":{"type":"object","properties":{"configured":{"type":"boolean"},"webhook_path":{"type":"string","description":"Path to register at the provider, appended to the API base URL"}},"required":["configured","webhook_path"]}},"required":["pakasir","creem","paypal"]}},"required":["providers"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/payments/{provider}":{"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set provider fields. Creem: `api_key`*, `webhook_secret`*, `product_id` (a one-time product), `mode` (`test`|`live`), `currency`. PayPal: `client_id`, `client_secret`*, `webhook_id`, `mode` (`sandbox`|`live`). (* = secret.) A blank secret keeps the stored value; a blank plain field clears it.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["creem","paypal"]},"required":true,"name":"provider","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"configured":{"type":"boolean"}},"required":["ok","configured"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/settings/payments/{provider}/check":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Test the saved credentials against the provider without creating a payment. Pakasir: slug + API key (an unknown transaction must answer \"not found\", not 401). Creem: API key in the saved mode, the product exists, is one-time and in the configured currency, webhook secret saved. PayPal: client ID/secret in the saved mode, the webhook exists, points at this gateway and is subscribed to the order/capture events. Each step: `ok` true, false, or null (no clear answer).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["pakasir","creem","paypal"]},"required":true,"name":"provider","in":"path"}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"mode":{"type":["string","null"],"enum":["live","test"]},"steps":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":["boolean","null"]},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["ok","mode","steps"]}}}},"404":{"description":"Unknown provider","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/settings/payments/{provider}/{field}":{"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Clear one provider field (e.g. to rotate a secret).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["creem","paypal"]},"required":true,"name":"provider","in":"path"},{"schema":{"type":"string"},"required":true,"name":"field","in":"path"}],"responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Unknown provider or field","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/plans":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"All plans with their price list (`prices`: currency → minor units) and their send rate (`send_per_minute`).","responses":{"200":{"description":"Plans","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"price_monthly":{"type":"integer"},"max_sessions":{"type":"integer"},"max_messages_per_month":{"type":"integer"},"overage_per_message":{"type":"integer"},"send_per_minute":{"type":"integer","description":"Requests per minute on `POST …messages…` (edit with `PUT /v1/admin/plans/{id}/rate`)"},"prices":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","name","price_monthly","max_sessions","max_messages_per_month","overage_per_message","send_per_minute","prices"]}}},"required":["plans"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/plans/{id}/prices":{"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set a plan's price in one currency (minor units). IDR is the plan's own `price_monthly` and cannot be set here. Overage is billed per 1 000 messages beyond the quota (0 = none).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"price_minor":{"type":"integer","minimum":0,"maximum":99999999},"overage_per_1000_minor":{"type":"integer","minimum":0,"maximum":99999999}},"required":["currency","price_minor"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"prices":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["ok","prices"]}}}},"404":{"description":"Plan not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/plans/{id}/rate":{"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set how many requests per minute the tenants on a plan may make on `POST` paths containing `messages` (the strict `messages` bucket, per API key). 1–600. It caps API calls, not WhatsApp speed: delivery is still paced by the device queue. Takes effect on the next request; recorded in the operator audit log as `plan.rate`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"send_per_minute":{"type":"integer","minimum":1,"maximum":600}},"required":["send_per_minute"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"send_per_minute":{"type":"integer"}},"required":["ok","send_per_minute"]}}}},"400":{"description":"Not an integer from 1 to 600 (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Plan not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/plans/{id}/prices/{currency}":{"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Stop selling a plan in a currency.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"required":true,"description":"ISO 4217 code, e.g. `USD`, `IDR`","name":"currency","in":"path"}],"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Price not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/settings/billing":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Billing settings: `annual_months_charged` = months charged for an annual period (default 10, i.e. two months free).","responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"type":"object","properties":{"annual_months_charged":{"type":"integer"}},"required":["annual_months_charged"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set the months charged for an annual period (2–12). Audited as `settings.billing`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"annual_months_charged":{"type":"integer","minimum":2,"maximum":12}},"required":["annual_months_charged"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"annual_months_charged":{"type":"integer"}},"required":["ok","annual_months_charged"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/coupons":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"All coupons (newest first, max 500) with how often each was redeemed.","responses":{"200":{"description":"Coupons","content":{"application/json":{"schema":{"type":"object","properties":{"coupons":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"percent_off":{"type":["integer","null"]},"amount_off_minor":{"type":["integer","null"]},"currency":{"type":["string","null"],"pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"plan_ids":{"type":["array","null"],"items":{"type":"integer"},"description":"null = every plan"},"billing_periods":{"type":["array","null"],"items":{"type":"string","enum":["monthly","annual"]},"description":"null = both"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"max_redemptions":{"type":["integer","null"]},"once_per_tenant":{"type":"boolean"},"active":{"type":"boolean"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"redemptions":{"type":"integer","description":"Paid invoices that used it"}},"required":["code","percent_off","amount_off_minor","currency","plan_ids","billing_periods","valid_from","valid_until","max_redemptions","once_per_tenant","active","note","created_at","redemptions"]}}},"required":["coupons"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Create a coupon. Exactly one of `percent_off` (1–100) or `amount_off_minor` (+ `currency`). Optional limits: `plan_ids`, `billing_periods`, `valid_from`/`valid_until` (ms), `max_redemptions`, `once_per_tenant` (default true). A redemption is counted when the invoice is paid. Audited as `coupon.create`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"3–32 letters, digits, - or _; stored upper-case"},"percent_off":{"type":"integer","minimum":1,"maximum":100},"amount_off_minor":{"type":"integer","minimum":1},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"plan_ids":{"type":"array","items":{"type":"integer"}},"billing_periods":{"type":"array","items":{"type":"string","enum":["monthly","annual"]}},"valid_from":{"type":"integer"},"valid_until":{"type":"integer"},"max_redemptions":{"type":"integer","minimum":1},"once_per_tenant":{"type":"boolean"},"note":{"type":"string","maxLength":200}},"required":["code"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"coupon":{"type":"object","properties":{"code":{"type":"string"},"percent_off":{"type":["integer","null"]},"amount_off_minor":{"type":["integer","null"]},"currency":{"type":["string","null"],"pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"plan_ids":{"type":["array","null"],"items":{"type":"integer"},"description":"null = every plan"},"billing_periods":{"type":["array","null"],"items":{"type":"string","enum":["monthly","annual"]},"description":"null = both"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"max_redemptions":{"type":["integer","null"]},"once_per_tenant":{"type":"boolean"},"active":{"type":"boolean"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"redemptions":{"type":"integer","description":"Paid invoices that used it"}},"required":["code","percent_off","amount_off_minor","currency","plan_ids","billing_periods","valid_from","valid_until","max_redemptions","once_per_tenant","active","note","created_at","redemptions"]}},"required":["ok","coupon"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The code already exists (`CONFLICT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/coupons/{code}":{"patch":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Switch a coupon on or off, or change its end date, maximum uses or note. Audited as `coupon.update`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"code","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"valid_until":{"type":["integer","null"]},"max_redemptions":{"type":["integer","null"],"minimum":1},"note":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"coupon":{"type":"object","properties":{"code":{"type":"string"},"percent_off":{"type":["integer","null"]},"amount_off_minor":{"type":["integer","null"]},"currency":{"type":["string","null"],"pattern":"^[A-Z]{3}$","description":"ISO 4217 code, e.g. `USD`, `IDR`"},"plan_ids":{"type":["array","null"],"items":{"type":"integer"},"description":"null = every plan"},"billing_periods":{"type":["array","null"],"items":{"type":"string","enum":["monthly","annual"]},"description":"null = both"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"max_redemptions":{"type":["integer","null"]},"once_per_tenant":{"type":"boolean"},"active":{"type":"boolean"},"note":{"type":["string","null"]},"created_at":{"type":"integer"},"redemptions":{"type":"integer","description":"Paid invoices that used it"}},"required":["code","percent_off","amount_off_minor","currency","plan_ids","billing_periods","valid_from","valid_until","max_redemptions","once_per_tenant","active","note","created_at","redemptions"]}},"required":["ok","coupon"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Delete a coupon that was never redeemed (a redeemed one is part of the invoice history: deactivate it instead, 409). Audited as `coupon.delete`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"Already redeemed (`CONFLICT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/2fa":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Whether two-factor sign-in is on for the platform secret. Readable with the secret alone. While it is on, every other `/v1/admin/*` call also needs `X-Admin-Session` (401 `ADMIN_2FA_REQUIRED` otherwise), except calls made on the server itself over loopback without proxy headers.","responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"recovery_codes_left":{"type":"integer"},"sessions":{"type":"integer","description":"Signed-in sessions"}},"required":["enabled","recovery_codes_left","sessions"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/setup":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Start enrolment: a new secret for an authenticator app (`otpauth` for a QR code). Nothing changes until `/enable` confirms a code. 409 while 2FA is on.","responses":{"200":{"description":"Pending secret","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string","description":"Base32, for typing into the app"},"otpauth":{"type":"string"}},"required":["secret","otpauth"]}}}},"409":{"description":"Already on","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/enable":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Confirm enrolment with a current code. Returns 10 one-time recovery codes (shown only now) and a session for this browser. Audited as `admin.2fa_enabled`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"6-digit authenticator code (or, for sign-in and turning off, a recovery code `XXXX-XXXX`)"}},"required":["code"]}}}},"responses":{"200":{"description":"On","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"string","description":"Send as `X-Admin-Session` together with `X-Admin-Secret`"},"expires_at":{"type":"integer"},"recovery_codes":{"type":"array","items":{"type":"string"}}},"required":["session","expires_at","recovery_codes"]}}}},"400":{"description":"Wrong code (`INVALID_CODE`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"Already on","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"5 wrong codes in 15 minutes (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/session":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Sign in: the secret plus a current authenticator code (each code works once) or a recovery code (consumed). Sessions last 12 hours, or until 2 hours without use. Audited as `admin.2fa_login`, `admin.2fa_recovery_used` or `admin.2fa_failed`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"6-digit authenticator code (or, for sign-in and turning off, a recovery code `XXXX-XXXX`)"}},"required":["code"]}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"string","description":"Send as `X-Admin-Session` together with `X-Admin-Secret`"},"expires_at":{"type":"integer"},"recovery_codes_left":{"type":"integer"}},"required":["session","expires_at","recovery_codes_left"]}}}},"401":{"description":"Wrong or reused code (`INVALID_CODE`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"2FA is not on","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"5 wrong codes in 15 minutes (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Sign out the session sent in `X-Admin-Session`.","responses":{"200":{"description":"Signed out","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/recovery-codes":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Replace the recovery codes (the old ones stop working). Needs a current authenticator code. Audited as `admin.2fa_recovery_regenerated`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"6-digit authenticator code (or, for sign-in and turning off, a recovery code `XXXX-XXXX`)"}},"required":["code"]}}}},"responses":{"200":{"description":"New codes","content":{"application/json":{"schema":{"type":"object","properties":{"recovery_codes":{"type":"array","items":{"type":"string"}}},"required":["recovery_codes"]}}}},"400":{"description":"Wrong code","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/disable":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Turn two-factor sign-in off (needs a code) and sign every session out. Audited as `admin.2fa_disabled`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"6-digit authenticator code (or, for sign-in and turning off, a recovery code `XXXX-XXXX`)"}},"required":["code"]}}}},"responses":{"200":{"description":"Off","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Wrong code","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/2fa/reset":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Lost phone and recovery codes: turn 2FA off without a code. Only accepted from the server itself (loopback, no proxy headers), e.g. `curl -X POST -H \"X-Admin-Secret: …\" http://127.0.0.1:3000/v1/admin/2fa/reset` over SSH. Audited as `admin.2fa_reset`.","responses":{"200":{"description":"Off","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"403":{"description":"Not from the server itself","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/billing/notifications":{"get":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Billing email of the tenant: invoices, payment receipts, \"plan ends in 3 days\" and \"plan ended\" are emailed there (as well as the WhatsApp reminder), in the language of the business profile.","responses":{"200":{"description":"Preferences","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"],"description":"Where invoices, receipts and plan reminders go; null = none"},"enabled":{"type":"boolean"},"available":{"type":"boolean","description":"Whether the platform has email set up at all"}},"required":["email","enabled","available"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the billing email settings"},"put":{"tags":["billing"],"security":[{"bearerAuth":[]}],"description":"Set the billing email (`null` or empty removes it) and/or switch billing emails on or off. Owner login only (403 for agents and API keys). Audited as `billing.notifications`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"]},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"],"description":"Where invoices, receipts and plan reminders go; null = none"},"enabled":{"type":"boolean"},"available":{"type":"boolean","description":"Whether the platform has email set up at all"}},"required":["email","enabled","available"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Update the billing email settings"}},"/v1/admin/settings/email":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"kirim.id email settings (the API key is write-only: `has_api_key`) and the outbox: counts for the last 7 days and the 20 most recent emails.","responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"boolean"},"has_api_key":{"type":"boolean"},"domain_id":{"type":"string"},"from_email":{"type":"string"},"from_name":{"type":"string"},"reply_to":{"type":"string"},"outbox":{"type":"object","properties":{"sent":{"type":"integer"},"pending":{"type":"integer"},"failed":{"type":"integer"},"recent":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"to_email":{"type":"string","description":"Masked"},"status":{"type":"string","enum":["pending","sending","sent","failed"]},"attempts":{"type":"integer"},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"},"sent_at":{"type":["integer","null"]}},"required":["id","kind","to_email","status","attempts","last_error","created_at","sent_at"]}}},"required":["sent","pending","failed","recent"]}},"required":["configured","has_api_key","domain_id","from_email","from_name","reply_to","outbox"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set kirim.id fields: `api_key` (secret, `api_kirim…`, scope `send_email`), `domain_id` (an active sending domain), `from_email`, `from_name`, `reply_to`. A blank secret keeps the stored value; a blank plain field clears it. Audited as `settings.email`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"configured":{"type":"boolean"}},"required":["ok","configured"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/email/{field}":{"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Clear one email field.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["api_key","domain_id","from_email","from_name","reply_to"]},"required":true,"name":"field","in":"path"}],"responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Unknown field","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/settings/email/check":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Test the saved API key against kirim.id (`GET /v1/account`): coin balance, speed tier, active sending domains, and whether the saved `domain_id` is one of them (`null` when kirim.id did not list domains).","responses":{"200":{"description":"Key works","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"domain_active":{"type":["boolean","null"]},"account":{"type":"object","properties":{"coins":{"type":["number","null"]},"premium":{"type":["boolean","null"]},"send_per_minute":{"type":["number","null"]},"send_per_day":{"type":["number","null"]},"domains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"domain":{"type":"string"}},"required":["id","domain"]}}},"required":["coins","premium","send_per_minute","send_per_day","domains"]}},"required":["ok","domain_active","account"]}}}},"400":{"description":"No key saved, or kirim.id refused it","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}},"502":{"description":"kirim.id unreachable","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/email/test":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Send a test email now (not through the outbox). Costs one kirim.id coin.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string"},"locale":{"type":"string","enum":["en","id"]}},"required":["to"]}}}},"responses":{"200":{"description":"Accepted by kirim.id","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"message_id":{"type":"string"}},"required":["ok","message_id"]}}}},"400":{"description":"Not set up (`NOT_CONFIGURED`) or bad address","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"kirim.id refused or failed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/ai-reply":{"get":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"AI auto-reply settings and this month's quota. AI answers a 1:1 message only when no auto-reply rule or chatbot flow did, and steps back in a chat for `pause_hours` after a person from the business sends a message from the dashboard.","responses":{"200":{"description":"State","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","maxLength":2000,"description":"Persona, tone, dos and don'ts"},"knowledge":{"type":"string","maxLength":12000,"description":"Facts the assistant may use: products, prices, hours, policies, FAQ"},"handoff_keywords":{"type":"array","items":{"type":"string"},"description":"A message containing one of these words pauses AI for the chat (lower-case, whole words)"},"handoff_message":{"type":"string","description":"Sent when a customer asks for a person (empty = say nothing)"},"pause_hours":{"type":"integer","minimum":0,"maximum":168,"description":"How long AI stays quiet in a chat after a person from the business replied or a handoff"},"max_per_chat_per_day":{"type":"integer","minimum":1,"maximum":200}},"required":["enabled","instructions","knowledge","handoff_keywords","handoff_message","pause_hours","max_per_chat_per_day"]},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]},"available":{"type":"boolean","description":"Whether the platform has an AI model set up"}},"required":["settings","quota","available"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get AI reply settings and quota"},"put":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Change AI auto-reply settings (any subset). Owner login only. Turning it on needs some `knowledge`, `instructions` or a ready knowledge document. Audited as `ai_reply.update`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","maxLength":2000,"description":"Persona, tone, dos and don'ts"},"knowledge":{"type":"string","maxLength":12000,"description":"Facts the assistant may use: products, prices, hours, policies, FAQ"},"handoff_keywords":{"type":"array","items":{"type":"string"},"description":"A message containing one of these words pauses AI for the chat (lower-case, whole words)"},"handoff_message":{"type":"string","description":"Sent when a customer asks for a person (empty = say nothing)"},"pause_hours":{"type":"integer","minimum":0,"maximum":168,"description":"How long AI stays quiet in a chat after a person from the business replied or a handoff"},"max_per_chat_per_day":{"type":"integer","minimum":1,"maximum":200}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","maxLength":2000,"description":"Persona, tone, dos and don'ts"},"knowledge":{"type":"string","maxLength":12000,"description":"Facts the assistant may use: products, prices, hours, policies, FAQ"},"handoff_keywords":{"type":"array","items":{"type":"string"},"description":"A message containing one of these words pauses AI for the chat (lower-case, whole words)"},"handoff_message":{"type":"string","description":"Sent when a customer asks for a person (empty = say nothing)"},"pause_hours":{"type":"integer","minimum":0,"maximum":168,"description":"How long AI stays quiet in a chat after a person from the business replied or a handoff"},"max_per_chat_per_day":{"type":"integer","minimum":1,"maximum":200}},"required":["enabled","instructions","knowledge","handoff_keywords","handoff_message","pause_hours","max_per_chat_per_day"]},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]},"available":{"type":"boolean","description":"Whether the platform has an AI model set up"}},"required":["settings","quota","available"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Update AI reply settings"}},"/v1/ai-reply/preview":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Ask the assistant one question with the saved settings, or with draft `settings` before saving. Counts as one AI reply. Owner only, 10 per minute.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","minLength":1,"maxLength":2000},"settings":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","maxLength":2000,"description":"Persona, tone, dos and don'ts"},"knowledge":{"type":"string","maxLength":12000,"description":"Facts the assistant may use: products, prices, hours, policies, FAQ"},"handoff_keywords":{"type":"array","items":{"type":"string"},"description":"A message containing one of these words pauses AI for the chat (lower-case, whole words)"},"handoff_message":{"type":"string","description":"Sent when a customer asks for a person (empty = say nothing)"},"pause_hours":{"type":"integer","minimum":0,"maximum":168,"description":"How long AI stays quiet in a chat after a person from the business replied or a handoff"},"max_per_chat_per_day":{"type":"integer","minimum":1,"maximum":200}}}},"required":["message"]}}}},"responses":{"200":{"description":"Answer","content":{"application/json":{"schema":{"type":"object","properties":{"reply":{"type":"string"},"ms":{"type":"integer"},"sources":{"type":"array","items":{"type":"string"},"description":"Names of the knowledge documents whose passages were given to the model; `:faq` stands for the business's saved answers"},"unsure":{"type":"boolean","description":"The model said the information does not answer the question (a customer would get this reply and the question would be listed as unanswered)"},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]}},"required":["reply","ms","sources","unsure","quota"]}}}},"402":{"description":"No AI replies left this month (`LIMIT_REACHED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"More than 10 previews a minute","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"The model did not answer (`UPSTREAM_ERROR`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"The platform has no AI model set up (`NOT_CONFIGURED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Try a question on the assistant"}},"/v1/ai-reply/chats/pause":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Stop AI replies in one chat for `hours` (1–720; default the tenant's `pause_hours`). Allowed for support agents.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"chat_id":{"type":"string"},"hours":{"type":"integer","minimum":1,"maximum":720}},"required":["session_id","chat_id"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Unknown session or missing chat_id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Pause AI replies in a chat"}},"/v1/ai-reply/chats/resume":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Let AI reply in this chat again.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"chat_id":{"type":"string"}},"required":["session_id","chat_id"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Unknown session or missing chat_id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Resume AI replies in a chat"}},"/v1/admin/settings/ai":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"The platform AI model: `provider` `openai` (any OpenAI-compatible API) or `anthropic` (any Anthropic-compatible Messages API), `base_url`, `model`, limits, and replies/tokens used by all tenants this month. The API key is write-only (`has_api_key`).","responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"boolean"},"enabled":{"type":"boolean"},"provider":{"type":"string","enum":["openai","anthropic"]},"base_url":{"type":"string"},"default_base_url":{"type":"string"},"model":{"type":"string"},"has_api_key":{"type":"boolean"},"max_tokens":{"type":"number"},"timeout_s":{"type":"number"},"temperature":{"type":"number"},"usage_this_month":{"type":"object","properties":{"replies":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"required":["replies","input_tokens","output_tokens"]}},"required":["configured","enabled","provider","base_url","default_base_url","model","has_api_key","max_tokens","timeout_s","temperature","usage_this_month"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set fields (all strings): `provider` (`openai`|`anthropic`), `base_url` (https; http only for a local or private-network model server), `model`, `api_key` (secret), `max_tokens` (32–4000), `timeout_s` (3–120), `temperature` (0–1.5), `enabled` (`true`|`false`, the platform-wide switch). A blank secret keeps the stored value; a blank plain field clears it. Audited as `settings.ai`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"configured":{"type":"boolean"}},"required":["ok","configured"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/ai/{field}":{"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Clear one AI field.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"field","in":"path"}],"responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"description":"Unknown field","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/settings/ai/test":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Call the configured model once (default prompt: say hello) and report the answer, latency and tokens.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Works","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"reply":{"type":"string"},"ms":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"model":{"type":"string"},"provider":{"type":"string"}},"required":["ok","reply","ms","input_tokens","output_tokens","model","provider"]}}}},"400":{"description":"Not set up","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}},"502":{"description":"The model call failed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/plans/{id}/ai":{"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"AI on a plan: replies included per month (0 = not included, up to 1 000 000) and knowledge documents (`kb_max_files`, `kb_max_mb`; 0 = not included). Send any subset. Audited as `plan.ai`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ai_replies_per_month":{"type":"integer","minimum":0,"maximum":1000000},"kb_max_files":{"type":"integer","minimum":0,"maximum":10000},"kb_max_mb":{"type":"integer","minimum":0,"maximum":100000}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ai_replies_per_month":{"type":"integer"},"kb_max_files":{"type":"integer"},"kb_max_mb":{"type":"integer"}},"required":["ok","ai_replies_per_month","kb_max_files","kb_max_mb"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Plan not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/ai-reply/documents":{"get":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Knowledge documents for AI replies: files the business uploaded (price lists, menus, policies, FAQs), their reading status, and the plan's limits. When a customer writes, the passages that best match the message are given to the AI with the typed knowledge — the AI answers from them and says a person will follow up when they do not cover the question.","responses":{"200":{"description":"State","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the platform offers knowledge documents (switched off by the operator = false)"},"limits":{"type":"object","properties":{"files":{"type":"integer","description":"Documents allowed by the plan (0 = not included)"},"mb":{"type":"integer"},"file_mb":{"type":"number","description":"Largest single file"},"types":{"type":"array","items":{"type":"string"}}},"required":["files","mb","file_mb","types"]},"used":{"type":"object","properties":{"files":{"type":"integer"},"mb":{"type":"number"}},"required":["files","mb"]},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}}},"required":["available","limits","used","documents"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List knowledge documents"},"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Upload one file (multipart field `file`): PDF with selectable text, Word (.docx), Excel (.xlsx), CSV, text, Markdown or HTML, up to 8 MB. The knowledge engine reads it in the background (`status` `parsing` → `ready`); when the engine is not set up, unreachable or fails the file, the backup reader on the API server reads it instead (`engine: local`, usually `ready` at once). Owner login only; 30 uploads (files and links) per 10 minutes. Audited as `ai_kb.upload`.","requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","description":"The file (binary)"}},"required":["file"]}}}},"responses":{"201":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the platform offers knowledge documents (switched off by the operator = false)"},"limits":{"type":"object","properties":{"files":{"type":"integer","description":"Documents allowed by the plan (0 = not included)"},"mb":{"type":"integer"},"file_mb":{"type":"number","description":"Largest single file"},"types":{"type":"array","items":{"type":"string"}}},"required":["files","mb","file_mb","types"]},"used":{"type":"object","properties":{"files":{"type":"integer"},"mb":{"type":"number"}},"required":["files","mb"]},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"document":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"required":["available","limits","used","documents","document"]}}}},"400":{"description":"`VALIDATION_FAILED`, `EMPTY_FILE`, `BAD_FILE`, `BAD_ENCODING`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`NOT_IN_PLAN` or `LIMIT_REACHED` (files or MB of the plan)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"`FILE_TOO_LARGE`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"415":{"description":"`UNSUPPORTED_TYPE`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many uploads","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"`ENGINE_ERROR` — the knowledge engine did not accept the request; try again","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"`NOT_CONFIGURED` or `PLATFORM_FULL`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Upload a knowledge document"}},"/v1/ai-reply/documents/{id}":{"delete":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Delete a document: AI replies stop using it at once. Owner login only. Audited as `ai_kb.delete`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the platform offers knowledge documents (switched off by the operator = false)"},"limits":{"type":"object","properties":{"files":{"type":"integer","description":"Documents allowed by the plan (0 = not included)"},"mb":{"type":"integer"},"file_mb":{"type":"number","description":"Largest single file"},"types":{"type":"array","items":{"type":"string"}}},"required":["files","mb","file_mb","types"]},"used":{"type":"object","properties":{"files":{"type":"integer"},"mb":{"type":"number"}},"required":["files","mb"]},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"ok":{"type":"boolean"}},"required":["available","limits","used","documents","ok"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Delete a knowledge document"}},"/v1/ai-reply/documents/{id}/retry":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Read a `failed` document again. Owner login only.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Reading again","content":{"application/json":{"schema":{"type":"object","properties":{"document":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"required":["document"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"`NOT_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"`ENGINE_ERROR` — the knowledge engine did not accept the request; try again","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"`NOT_CONFIGURED` or `PLATFORM_FULL`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Read a failed document again"}},"/v1/ai-reply/documents/search":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"The document passages an AI reply would get for `question` — to check that a document answers what customers ask. Does not call the AI and does not use the AI quota. Owner login only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":2000}},"required":["question"]}}}},"responses":{"200":{"description":"Passages, best first","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"document":{"type":"string","description":"Document name, or `:faq` for a saved answer"},"text":{"type":"string"},"faq":{"type":"boolean"}},"required":["document","text"]}},"ms":{"type":"number"}},"required":["results","ms"]}}}},"400":{"description":"`VALIDATION_FAILED`, `EMPTY_FILE`, `BAD_FILE`, `BAD_ENCODING`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Search the knowledge documents"}},"/v1/ai-reply/documents/url":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Add a public web page (FAQ, shipping, prices) as a document: its text is read once (HTML or plain text, up to 3 MB, up to 4 redirects; private and local addresses are refused) and kept with `Source:` and `Title:` lines; `name` is the page title and `source_url` the final address. Pages that need JavaScript to show their text cannot be read (`NO_TEXT`). Counts as one file of the plan. Owner login only; shares the upload limit. Audited as `ai_kb.upload` (detail `link <url>`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2000,"format":"uri"}},"required":["url"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the platform offers knowledge documents (switched off by the operator = false)"},"limits":{"type":"object","properties":{"files":{"type":"integer","description":"Documents allowed by the plan (0 = not included)"},"mb":{"type":"integer"},"file_mb":{"type":"number","description":"Largest single file"},"types":{"type":"array","items":{"type":"string"}}},"required":["files","mb","file_mb","types"]},"used":{"type":"object","properties":{"files":{"type":"integer"},"mb":{"type":"number"}},"required":["files","mb"]},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"document":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"required":["available","limits","used","documents","document"]}}}},"400":{"description":"`VALIDATION_FAILED` or `URL_BLOCKED` (private / local address)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`NOT_IN_PLAN` or `LIMIT_REACHED` (files or MB of the plan)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"`FILE_TOO_LARGE` — page over 3 MB","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"415":{"description":"`UNSUPPORTED_TYPE` — not a web page or text (download the file and upload it)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"422":{"description":"`FETCH_FAILED` (unreachable, HTTP error, too many redirects) or `NO_TEXT`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many uploads","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"`NOT_CONFIGURED` or `PLATFORM_FULL`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Add a web page as a document"}},"/v1/ai-reply/documents/{id}/refresh":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Read a web document's page again (after the page changed). The new copy gets a new id and replaces the old one once it is in; the plan limit counts them as one. Owner login only. Audited as `ai_kb.upload` (detail `refresh <url>`).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"201":{"description":"Read again","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the platform offers knowledge documents (switched off by the operator = false)"},"limits":{"type":"object","properties":{"files":{"type":"integer","description":"Documents allowed by the plan (0 = not included)"},"mb":{"type":"integer"},"file_mb":{"type":"number","description":"Largest single file"},"types":{"type":"array","items":{"type":"string"}}},"required":["files","mb","file_mb","types"]},"used":{"type":"object","properties":{"files":{"type":"integer"},"mb":{"type":"number"}},"required":["files","mb"]},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"document":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"File name as uploaded"},"ext":{"type":"string","description":"`pdf`, `docx`, `xlsx`, `csv`, `txt`, `md` or `html`"},"size":{"type":"integer","description":"Bytes"},"status":{"type":"string","enum":["uploading","parsing","ready","failed","deleting"],"description":"`parsing` while the engine reads the file (seconds to a few minutes), `ready` when AI replies use it, `failed` with `error` (e.g. a scanned PDF without text), `deleting` while the engine copy is removed"},"progress":{"type":"number","description":"0–1 while parsing"},"chunks":{"type":"integer","description":"Passages the file was cut into"},"error":{"type":["string","null"]},"engine":{"type":"string","enum":["ragflow","local"],"description":"Who read it: `ragflow` (the knowledge engine) or `local` (the backup reader on the API server, used when the engine is not set up, unreachable or failed the file)"},"source_url":{"type":["string","null"],"description":"The web page a document was read from (`POST /v1/ai-reply/documents/url`); `name` is then the page title"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","name","ext","size","status","progress","chunks","error","engine","source_url","created_at","updated_at"]}},"required":["available","limits","used","documents","document"]}}}},"400":{"description":"`URL_BLOCKED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"`NOT_A_PAGE` — the document was uploaded as a file","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"422":{"description":"`FETCH_FAILED` or `NO_TEXT`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Read a web page again"}},"/v1/ai-reply/faq":{"get":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Saved answers: question/answer pairs written by the business. AI replies find them like document passages and prefer them (the business wrote them for exactly that question). Readable by agents.","responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"source":{"type":"string","enum":["manual","unanswered"],"description":"`unanswered`: written from a question the assistant could not answer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","question","answer","source","created_at","updated_at"]}}},"required":["items"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List saved answers"},"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Add a saved answer (at most 500 per business). Used by the next AI reply at once. Owner login only. Audited as `ai_kb.faq` (detail `add`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":500},"answer":{"type":"string","minLength":1,"maxLength":2000}},"required":["question","answer"]}}}},"responses":{"201":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"faq":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"source":{"type":"string","enum":["manual","unanswered"],"description":"`unanswered`: written from a question the assistant could not answer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","question","answer","source","created_at","updated_at"]}},"required":["faq"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`LIMIT_REACHED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Add a saved answer"}},"/v1/ai-reply/faq/{id}":{"put":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Change a saved answer. Owner login only. Audited as `ai_kb.faq` (detail `edit`).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":500},"answer":{"type":"string","minLength":1,"maxLength":2000}},"required":["question","answer"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"faq":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"source":{"type":"string","enum":["manual","unanswered"],"description":"`unanswered`: written from a question the assistant could not answer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","question","answer","source","created_at","updated_at"]}},"required":["faq"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Change a saved answer"},"delete":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Delete a saved answer. Owner login only. Audited as `ai_kb.faq` (detail `delete`).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Delete a saved answer"}},"/v1/ai-reply/unanswered":{"get":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Questions the AI assistant could not answer from the business information (it told the customer the team will follow up). Open ones first, most asked first; `status=all` also lists answered and dismissed ones. At most 300 open per business (the least asked make room). Readable by agents.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["open","all"]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"List","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string","description":"The customer's message (first wording seen)"},"count":{"type":"integer","description":"How many times it was asked (same words, ignoring case and punctuation)"},"session_id":{"type":["string","null"]},"chat_id":{"type":["string","null"],"description":"The latest chat that asked"},"reply":{"type":["string","null"],"description":"What the assistant answered"},"status":{"type":"string","enum":["open","answered","dismissed"]},"faq_id":{"type":["string","null"]},"created_at":{"type":"integer"},"last_at":{"type":"integer"}},"required":["id","question","count","session_id","chat_id","reply","status","faq_id","created_at","last_at"]}},"open":{"type":"integer"}},"required":["items","open"]}}}}},"summary":"List unanswered questions"}},"/v1/ai-reply/unanswered/{id}/answer":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Answer a question once: it becomes a saved answer (optionally with a cleaner `question`) that the next AI reply uses, and leaves the open list. Owner login only. Audited as `ai_kb.faq` (detail `answer`).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"answer":{"type":"string","minLength":1,"maxLength":2000},"question":{"type":"string","minLength":1,"maxLength":500}},"required":["answer"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"faq":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"source":{"type":"string","enum":["manual","unanswered"],"description":"`unanswered`: written from a question the assistant could not answer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","question","answer","source","created_at","updated_at"]},"open":{"type":"integer"}},"required":["ok","faq","open"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`LIMIT_REACHED` (500 saved answers)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Answer an unanswered question"}},"/v1/ai-reply/unanswered/{id}/dismiss":{"post":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"Close an open question without answering it. Owner login only.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Closed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"open":{"type":"integer"}},"required":["ok","open"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found or not open","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Dismiss an unanswered question"}},"/v1/ai-reply/report":{"get":{"tags":["auto-reply"],"security":[{"bearerAuth":[]}],"description":"What the AI assistant did over the last 7, 30 or 90 days (default 30), by day in the business's time zone: replies, replies where it was unsure, handoffs to a person, chats served, answered rate (`replies / all`), most asked questions, documents used most, open unanswered questions, and this month's quota. Kept 90 days.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["7","30","90"]},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"Report","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer"},"totals":{"type":"object","properties":{"replies":{"type":"integer"},"unsure":{"type":"integer"},"handoffs":{"type":"integer"},"chats":{"type":"integer"},"answered_rate":{"type":["number","null"]}},"required":["replies","unsure","handoffs","chats","answered_rate"]},"daily":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","description":"YYYY-MM-DD"},"replies":{"type":"integer"},"unsure":{"type":"integer"},"handoffs":{"type":"integer"}},"required":["day","replies","unsure","handoffs"]}},"top_questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"count":{"type":"integer"},"unsure":{"type":"integer"}},"required":["question","count","unsure"]}},"top_sources":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","description":"Document name or `:faq`"},"count":{"type":"integer"}},"required":["source","count"]}},"open_unanswered":{"type":"integer"},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]}},"required":["days","totals","daily","top_questions","top_sources","open_unanswered","quota"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Get the AI report"}},"/v1/ai-reply/suggest":{"post":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"A draft of the next reply in a chat for the person answering it, from the last week of the conversation, the business information and the documents. Nothing is sent. Counts as one AI reply. Owner, and agents for chats assigned to them or unassigned.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"chat_id":{"type":"string"},"lang":{"type":"string","enum":["en","id"],"description":"Summary language (default en)"}},"required":["session_id","chat_id"]}}}},"responses":{"200":{"description":"Draft","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"},"ms":{"type":"integer"},"sources":{"type":"array","items":{"type":"string"}},"unsure":{"type":"boolean","description":"The business information did not answer it: check before sending"},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]}},"required":["text","ms","sources","unsure","quota"]}}}},"400":{"description":"`VALIDATION_FAILED` (session not yours, chat missing)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`LIMIT_REACHED` — no AI replies left this month","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"An agent asking about a chat assigned to someone else","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"422":{"description":"`NOTHING_TO_DO` — no recent text messages in the chat","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"More than 20 a minute per person","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"`UPSTREAM_ERROR`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"`NOT_CONFIGURED` — no AI model on the platform","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Suggest a reply for a chat"}},"/v1/ai-reply/summary":{"post":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"A 3–5 line summary of a conversation (last two weeks) for whoever takes it over: what the customer wants, what was promised, open questions, next step. Counts as one AI reply. Same access as `suggest`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"chat_id":{"type":"string"},"lang":{"type":"string","enum":["en","id"],"description":"Summary language (default en)"}},"required":["session_id","chat_id"]}}}},"responses":{"200":{"description":"Summary","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"},"ms":{"type":"integer"},"quota":{"type":"object","properties":{"limit":{"type":"integer","description":"AI replies included in the plan per month (0 = not included)"},"used":{"type":"integer"},"period":{"type":"string","description":"`YYYY-MM`"}},"required":["limit","used","period"]}},"required":["text","ms","quota"]}}}},"400":{"description":"`VALIDATION_FAILED` (session not yours, chat missing)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`LIMIT_REACHED` — no AI replies left this month","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"An agent asking about a chat assigned to someone else","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"422":{"description":"`NOTHING_TO_DO` — no recent text messages in the chat","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"More than 20 a minute per person","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"`UPSTREAM_ERROR`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"`NOT_CONFIGURED` — no AI model on the platform","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Summarize a chat"}},"/v1/push/key":{"get":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"The platform's VAPID public key, to pass as `applicationServerKey` to `PushManager.subscribe()`.","responses":{"200":{"description":"Key","content":{"application/json":{"schema":{"type":"object","properties":{"public_key":{"type":"string","description":"base64url, uncompressed P-256 point"}},"required":["public_key"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Get the push public key"}},"/v1/push/subscribe":{"post":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"Notify this browser when a customer writes in a 1:1 chat: the owner for every chat, an agent for chats assigned to them or unassigned. At most one notification per chat and device every 30 s; the preview is encrypted for the device. Dashboard logins only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri","description":"From PushManager.subscribe(); only the browsers' push services are accepted"},"keys":{"type":"object","properties":{"p256dh":{"type":"string"},"auth":{"type":"string"}},"required":["p256dh","auth"]}},"required":["endpoint","keys"]}}}},"responses":{"201":{"description":"Subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not a dashboard login","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Turn on notifications for a browser"},"delete":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"Stop notifications on this browser.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string"}},"required":["endpoint"]}}}},"responses":{"200":{"description":"Stopped","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"403":{"description":"Not a dashboard login","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Turn off notifications for a browser"}},"/v1/push/test":{"post":{"tags":["inbox"],"security":[{"bearerAuth":[]}],"description":"Send a test notification to this browser (header `X-Lang: id` for Indonesian).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string"}},"required":["endpoint"]}}}},"responses":{"200":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"403":{"description":"Not a dashboard login","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"This browser is not subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"`PUSH_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Send a test notification"}},"/v1/store-integrations":{"get":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Connected online stores. Each store sends its order webhooks to `webhook_url`; the customer gets a WhatsApp message from `session_id`: new order (`order_created`), payment received (`order_paid`), shipped with tracking (`order_shipped`) and, for Shopify, a reminder for an unfinished checkout (`abandoned_checkout`, after `abandoned_delay_min`, cancelled when the order arrives). Each message goes once per order through the scheduled queue, so sending limits and opt-outs apply. Owner login only.","responses":{"200":{"description":"Stores","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["shopify","woocommerce"]},"name":{"type":"string","description":"Store name used as {store}"},"session_id":{"type":["string","null"],"description":"The WhatsApp number that sends"},"enabled":{"type":"boolean"},"has_secret":{"type":"boolean","description":"Whether the webhook signing secret is saved (never returned)"},"webhook_url":{"type":"string","description":"Paste into the store's webhook settings"},"templates":{"type":"object","properties":{"order_created":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_paid":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_shipped":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"abandoned_checkout":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]}},"required":["order_created","order_paid","order_shipped","abandoned_checkout"]},"abandoned_delay_min":{"type":"integer"},"abandoned_only_marketing":{"type":"boolean"},"shop_domain":{"type":["string","null"],"description":"As reported by the store's webhooks"},"last_event_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","platform","name","session_id","enabled","has_secret","webhook_url","templates","abandoned_delay_min","abandoned_only_marketing","shop_domain","last_event_at","last_error","created_at"]}},"events":{"type":"array","items":{"type":"string"}}},"required":["items","events"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"List connected stores"},"post":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Connect a store (at most 5). Shopify: create the webhooks in Shopify (Settings → Notifications → Webhooks, JSON) with `webhook_url`, then save the signing secret Shopify shows there with `PUT … {secret}`. WooCommerce: a strong `secret` is generated and returned once — paste it as the webhook secret in WooCommerce (Settings → Advanced → Webhooks, topics Order created / Order updated). Audited as `store.connect`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["shopify","woocommerce"]},"name":{"type":"string","minLength":1,"maxLength":80},"session_id":{"type":"string"},"secret":{"type":"string","minLength":16,"maxLength":200}},"required":["platform","name"]}}}},"responses":{"201":{"description":"Connected","content":{"application/json":{"schema":{"type":"object","properties":{"integration":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["shopify","woocommerce"]},"name":{"type":"string","description":"Store name used as {store}"},"session_id":{"type":["string","null"],"description":"The WhatsApp number that sends"},"enabled":{"type":"boolean"},"has_secret":{"type":"boolean","description":"Whether the webhook signing secret is saved (never returned)"},"webhook_url":{"type":"string","description":"Paste into the store's webhook settings"},"templates":{"type":"object","properties":{"order_created":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_paid":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_shipped":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"abandoned_checkout":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]}},"required":["order_created","order_paid","order_shipped","abandoned_checkout"]},"abandoned_delay_min":{"type":"integer"},"abandoned_only_marketing":{"type":"boolean"},"shop_domain":{"type":["string","null"],"description":"As reported by the store's webhooks"},"last_event_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","platform","name","session_id","enabled","has_secret","webhook_url","templates","abandoned_delay_min","abandoned_only_marketing","shop_domain","last_event_at","last_error","created_at"]},"secret":{"type":"string","description":"WooCommerce: the generated signing secret, shown only now"}},"required":["integration"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"402":{"description":"`LIMIT_REACHED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}],"summary":"Connect a store"}},"/v1/store-integrations/{id}":{"put":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Change any of `name`, `session_id` (null = none), `secret` (blank keeps it), `enabled`, `templates` (any subset of the four messages), `abandoned_delay_min` (15–2880), `abandoned_only_marketing` (remind only buyers who accepted marketing). Audited as `store.update`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"session_id":{"type":["string","null"]},"secret":{"type":"string"},"enabled":{"type":"boolean"},"templates":{"type":"object","properties":{"order_created":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_paid":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_shipped":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"abandoned_checkout":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]}}},"abandoned_delay_min":{"type":"integer","minimum":15,"maximum":2880},"abandoned_only_marketing":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"integration":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["shopify","woocommerce"]},"name":{"type":"string","description":"Store name used as {store}"},"session_id":{"type":["string","null"],"description":"The WhatsApp number that sends"},"enabled":{"type":"boolean"},"has_secret":{"type":"boolean","description":"Whether the webhook signing secret is saved (never returned)"},"webhook_url":{"type":"string","description":"Paste into the store's webhook settings"},"templates":{"type":"object","properties":{"order_created":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_paid":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"order_shipped":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]},"abandoned_checkout":{"type":"object","properties":{"enabled":{"type":"boolean"},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"Placeholders: {name} {order} {total} {items} {status} {courier} {tracking_number} {tracking_url} {checkout_url} {store}; a line whose placeholders are all empty is left out"}},"required":["enabled","text"]}},"required":["order_created","order_paid","order_shipped","abandoned_checkout"]},"abandoned_delay_min":{"type":"integer"},"abandoned_only_marketing":{"type":"boolean"},"shop_domain":{"type":["string","null"],"description":"As reported by the store's webhooks"},"last_event_at":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","platform","name","session_id","enabled","has_secret","webhook_url","templates","abandoned_delay_min","abandoned_only_marketing","shop_domain","last_event_at","last_error","created_at"]}},"required":["integration"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Update a store connection"},"delete":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Disconnect a store: its link stops working and waiting checkout reminders are dropped. Audited as `store.disconnect`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Disconnect a store"}},"/v1/store-integrations/{id}/events":{"get":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"The last 50 webhooks from the store and what was done: `sent`, `scheduled` / `rescheduled` / `cancelled` (checkout reminders), `duplicate`, `off` (message switched off), `no_phone`, `no_session`, `not_marketing`, `ignored`. Kept 30 days.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Newest first","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"topic":{"type":"string"},"ref":{"type":["string","null"],"description":"Order number or checkout"},"outcome":{"type":"string"},"detail":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","topic","ref","outcome","detail","created_at"]}}},"required":["items"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"List recent store events"}},"/v1/store-integrations/{id}/test":{"post":{"tags":["settings"],"security":[{"bearerAuth":[]}],"description":"Send one message (`event`, default `order_created`) filled with a sample order to `phone`, through the queue. 10 per hour per store.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"event":{"type":"string","enum":["order_created","order_paid","order_shipped","abandoned_checkout"]}},"required":["phone"]}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"scheduled_id":{"type":"string"}},"required":["ok","scheduled_id"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"`NO_SESSION` — choose the sending number first","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many tests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Send a test store message"}},"/hooks/store/{token}":{"post":{"tags":["webhooks"],"description":"Where Shopify / WooCommerce send order webhooks (`webhook_url`). No bearer token: the body must be signed with the store's secret (HMAC-SHA256, base64) in `X-Shopify-Hmac-Sha256` or `X-WC-Webhook-Signature`; the topic comes from `X-Shopify-Topic` / `X-WC-Webhook-Topic`. WooCommerce's address check (`webhook_id=<n>`) is answered with 200. Shopify topics: orders/create, orders/paid, orders/fulfilled, orders/partially_fulfilled, fulfillments/create, fulfillments/update, checkouts/create, checkouts/update, checkouts/delete, orders/cancelled. WooCommerce: order.created, order.updated (processing = paid, completed = shipped; tracking from Shipment Tracking / AfterShip order meta).","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Handled","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"outcome":{"type":"string"}},"required":["ok"]}}}},"401":{"description":"`BAD_SIGNATURE` or `NO_SECRET`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown link","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"More than 300 a minute","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"summary":"Receive a store webhook"}},"/v1/admin/settings/knowledge":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"The knowledge engine (RAGFlow): `base_url`, whether an API key is set (write-only), where each value comes from (`settings`, `env` or `none`), the platform storage cap and what all businesses use.","responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"boolean"},"enabled":{"type":"boolean"},"base_url":{"type":"string"},"has_api_key":{"type":"boolean"},"url_safe":{"type":"boolean"},"source":{"type":"object","properties":{"base_url":{"type":"string","enum":["settings","env","none"]},"api_key":{"type":"string","enum":["settings","env","none"]}},"required":["base_url","api_key"]},"max_total_mb":{"type":"integer"},"usage":{"type":"object","properties":{"tenants":{"type":"integer"},"documents":{"type":"integer"},"bytes":{"type":"integer"},"chunks":{"type":"integer"},"local":{"type":"integer","description":"Documents read by the backup reader"},"mb":{"type":"number"}},"required":["tenants","documents","bytes","chunks","local","mb"]},"engine":{"type":"object","properties":{"state":{"type":"string","enum":["up","down","unknown"],"description":"From the gateway's own calls: `down` after a network error or HTTP 502-504; while down (2 min after the last failure) uploads go straight to the backup reader"},"since":{"type":"integer"},"last_error":{"type":["string","null"]},"last_ok_at":{"type":["integer","null"]}},"required":["state","since","last_error","last_ok_at"]}},"required":["configured","enabled","base_url","has_api_key","url_safe","source","max_total_mb","usage","engine"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Set fields (all strings): `base_url` (https, or http to this server / a private network such as the SSH tunnel `http://127.0.0.1:19380`), `api_key` (secret), `enabled` (`true`|`false`), `max_total_mb` (platform storage cap, ≥ 10). A blank secret keeps the stored value; a blank plain field clears it (the environment value applies again). Audited as `settings.knowledge`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"configured":{"type":"boolean"}},"required":["ok","configured"]}}}},"400":{"description":"`VALIDATION_FAILED`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/knowledge/test":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"description":"Ask the engine for its datasets: proves the URL and key work, with the latency and how many business datasets exist.","responses":{"200":{"description":"Works","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"ms":{"type":"integer"},"datasets":{"type":"integer"},"documents":{"type":"integer"},"usage":{"type":"object","properties":{"tenants":{"type":"integer"},"documents":{"type":"integer"},"bytes":{"type":"integer"},"chunks":{"type":"integer"},"mb":{"type":"number"}},"required":["tenants","documents","bytes","chunks","mb"]}},"required":["ok","ms","datasets","documents","usage"]}}}},"400":{"description":"Not set up","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["ok","error"]}}}},"502":{"description":"The engine did not answer","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"ms":{"type":"integer"},"error":{"type":"string"}},"required":["ok","ms","error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/sessions/{sessionId}/qr":{"get":{"tags":["sessions"],"summary":"Get the current QR code","description":"Returns the QR payload for linking the device: render the `qr` string with any QR library and scan it from WhatsApp (Linked devices). A code lasts about 60 seconds; `qr` is null while none is available, so keep polling until `status` is `qr_ready`. Answers 410 once the device is connected.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Current QR state","content":{"application/json":{"schema":{"type":"object","properties":{"qr":{"type":["string","null"],"description":"Raw QR payload string (not an image); null when there is no valid code right now"},"expires_at":{"type":["integer","null"],"description":"Expiry of the code (Unix epoch, milliseconds)"},"status":{"type":"string","description":"Session status: `pending`, `qr_ready`, `connected`, `disconnected` or `failed`"}},"required":["qr","expires_at","status"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"410":{"description":"The device is already connected (`error`: `Already connected`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/profile":{"get":{"tags":["sessions"],"summary":"Get the WhatsApp profile of a device","description":"Returns the linked account: number, display name, picture, About text and, for Business accounts, the business profile and catalog. A connected device is queried live with a 10-second limit, and parts that do not answer in time come back null or empty (`syncing: true` marks a timed-out lookup). A device that is not connected returns the cached values with `connected: false`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"type":"object","properties":{"connected":{"type":"boolean"},"syncing":{"type":"boolean","description":"`true` when the live lookup timed out and only partial data is returned"},"phone_number":{"type":["string","null"],"description":"Digits with country code, no `+`"},"wa_name":{"type":["string","null"]},"profile_pic_url":{"type":["string","null"]},"about":{"type":["string","null"],"description":"The About / status text"},"is_business":{"type":"boolean"},"business_profile":{"type":["object","null"],"properties":{"description":{"type":["string","null"]},"category":{"type":["string","null"]},"email":{"type":["string","null"]},"website":{"type":"array","items":{}},"address":{"type":["string","null"]},"business_hours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"mode":{"type":"string"},"open":{"type":["string","null"],"description":"`HH:MM`"},"close":{"type":["string","null"],"description":"`HH:MM`"}},"required":["day","mode","open","close"]}}},"required":["description","category","email","website","address","business_hours"],"description":"Only for Business accounts"},"catalog":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"price":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"image":{"type":["string","null"]},"url":{"type":["string","null"]}},"required":["id","name","price","currency","description","image","url"]},"description":"Visible catalog products (Business accounts only; empty otherwise)"}},"required":["connected","phone_number","wa_name","profile_pic_url","about","is_business","business_profile","catalog"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/pairing-code":{"post":{"tags":["sessions"],"summary":"Request a phone-number pairing code","description":"Links the device with a code instead of a QR scan: enter the returned `code` on the phone (WhatsApp, Linked devices, Link with phone number). Needs a session that has a live connection manager, so create it with `POST /v1/sessions` or call `/reconnect` first.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"type":"string","description":"Number of the WhatsApp account to link, with country code. Everything but digits is stripped; local-format numbers are not resolved"}},"required":["phone_number"]}}}},"responses":{"200":{"description":"Pairing code","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Code to type on the phone"}},"required":["code"]}}}},"400":{"description":"`phone_number` is missing","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No live connection manager for this session; unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"The library could not create a code; `error` carries its message","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/reconnect":{"post":{"tags":["sessions"],"summary":"Reconnect a device","description":"Restarts the WhatsApp connection of the session. With `clear_creds: true` the stored credentials are wiped first and the status goes back to `pending`, so the device has to be linked again (QR or pairing code); this flag only takes effect when the session already has a live connection manager. Poll `GET /v1/sessions/{id}` for the resulting status.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"clear_creds":{"type":"boolean","description":"Wipe the stored WhatsApp credentials before reconnecting (default false)"}}}}}},"responses":{"200":{"description":"Reconnect started","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/sync":{"post":{"tags":["sessions"],"summary":"Resync groups and chats","description":"Re-fetches the participating groups and the WhatsApp app-state (chats, contacts) and refreshes group names in the inbox; waits up to 15 seconds for the app-state part. `contacts` counts the one-to-one chats known in the inbox, not an address book.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Sync finished","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"integer","description":"Groups the account participates in"},"contacts":{"type":"integer","description":"Distinct one-to-one chats in the inbox"}},"required":["groups","contacts"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected (`NOT_CONNECTED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"502":{"description":"The sync failed (`SYNC_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/validate":{"post":{"tags":["sessions"],"summary":"Check numbers on WhatsApp","description":"Checks which of up to 1 000 numbers or JIDs have a WhatsApp account. Targets are normalised like recipients elsewhere (international format, or local format resolved against the business country or `country_code`); targets that cannot be normalised are dropped. If the lookup itself fails, every target is reported in `not_registered`. Limited to 10 calls per minute per key.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"string"},"maxItems":1000,"description":"Phone numbers or JIDs"},"country_code":{"type":"string","description":"Calling code (digits) used for local-format numbers; defaults to the business country"}},"required":["targets"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"registered":{"type":"array","items":{"type":"string"},"description":"JIDs that have a WhatsApp account"},"not_registered":{"type":"array","items":{"type":"string"},"description":"Normalised JIDs that were not found"}},"required":["registered","not_registered"]}}}},"400":{"description":"`targets` is not an array of strings, or has more than 1 000 entries","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: 10 requests per minute (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/search":{"get":{"tags":["messages"],"summary":"Search message text","description":"Substring search over the message bodies of one session, newest first. Matching is case-insensitive for ASCII letters; `%` and `_` in `q` are literal.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Text to search for (required, trimmed)"},"required":true,"description":"Text to search for (required, trimmed)","name":"q","in":"query"},{"schema":{"type":"string","description":"Only this chat (exact JID)"},"required":false,"description":"Only this chat (exact JID)","name":"chat_id","in":"query"},{"schema":{"type":"string","enum":["inbound","outbound"],"description":"Other values are ignored"},"required":false,"description":"Other values are ignored","name":"direction","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Values outside 1-200 are clamped"},"required":false,"description":"Values outside 1-200 are clamped","name":"limit","in":"query"}],"responses":{"200":{"description":"Matches","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageLogSummary"},"description":"Newest first"},"count":{"type":"integer"}},"required":["messages","count"]}}}},"400":{"description":"`q` is missing or empty (`INVALID`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{id}/context":{"get":{"tags":["messages"],"summary":"Get a message with its surroundings","description":"Returns the message together with the messages around it from the same chat, oldest first. Messages with exactly the same timestamp as the anchor are left out.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id (the `id` of a message in the history)"},"required":true,"description":"Gateway message id (the `id` of a message in the history)","name":"id","in":"path"},{"schema":{"type":"integer","minimum":0,"maximum":50,"description":"Messages before the anchor (default 10, clamped to 0-50)"},"required":false,"description":"Messages before the anchor (default 10, clamped to 0-50)","name":"before","in":"query"},{"schema":{"type":"integer","minimum":0,"maximum":50,"description":"Messages after the anchor (default 10, clamped to 0-50)"},"required":false,"description":"Messages after the anchor (default 10, clamped to 0-50)","name":"after","in":"query"}],"responses":{"200":{"description":"Anchor message and neighbours","content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageLogSummary"},"description":"Oldest first, including the anchor"}},"required":["chat_id","messages"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{id}/media":{"get":{"tags":["messages"],"summary":"Download stored media","description":"Returns the media file stored for a message as raw bytes. The response is always `application/octet-stream`; the original MIME type is not sent.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id (the `id` of a message in the history)"},"required":true,"description":"Gateway message id (the `id` of a message in the history)","name":"id","in":"path"}],"responses":{"200":{"description":"The media file (`application/octet-stream`)","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found, the message has no media, or the file is gone (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/cancel":{"post":{"tags":["messages"],"summary":"Cancel queued messages","description":"Marks messages that are still `queued` as `cancelled` and removes them from the send queue. Messages in any other status are left alone; duplicate ids are ignored.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":500,"description":"Gateway message ids"}},"required":["ids"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"integer","description":"Messages that were actually cancelled"}},"required":["cancelled"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/delete":{"post":{"tags":["messages"],"summary":"Delete messages from history","description":"Removes messages from the stored history and from the send queue. Nothing is unsent on WhatsApp; use `/messages/unsend` for that.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":500,"description":"Gateway message ids"}},"required":["ids"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"History rows removed"}},"required":["deleted"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/unsend":{"post":{"tags":["messages"],"summary":"Unsend messages for everyone","description":"Revokes your own sent messages on WhatsApp (\"delete for everyone\") and marks them `deleted` in the history. Each id is processed on its own and the call answers 200 even if some fail: check `results[].error`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":100,"description":"Gateway message ids"}},"required":["ids"]}}}},"responses":{"200":{"description":"Per-message result","content":{"application/json":{"schema":{"type":"object","properties":{"unsent":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":"string","enum":["not_found","not_sent","not_connected","wa_error"],"description":"`not_found`: unknown id; `not_sent`: inbound message or not yet handed to WhatsApp; `not_connected`: session offline; `wa_error`: WhatsApp refused"}},"required":["id","ok"]}}},"required":["unsent","failed","results"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/forward":{"post":{"tags":["messages"],"summary":"Forward a message to another chat","description":"Sends the stored type, text and media key of a logged message to another chat as a new message. It goes through the same opt-out check as `POST /messages`: an opted-out recipient yields a `RECIPIENT_OPTED_OUT` warning, or a 409 when strict mode (`enforce_api`) is on.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id to forward"},"required":true,"description":"Gateway message id to forward","name":"msgId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to_chat_id":{"type":"string","description":"Destination: a JID or a phone number (local-format numbers are resolved against the business country)"}},"required":["to_chat_id"]}}}},"responses":{"200":{"description":"Forwarded","content":{"application/json":{"schema":{"type":"object","properties":{"message_id":{"type":"string","description":"Id of the new message"},"ok":{"type":"boolean"},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"description":"Present when the recipient has opted out (`RECIPIENT_OPTED_OUT`): the message was still accepted because strict mode (`enforce_api`) is off"}},"required":["message_id","ok"]}}}},"400":{"description":"`to_chat_id` missing (`VALIDATION_FAILED`), or invalid (`INVALID_NUMBER`, `INVALID_JID`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"The message was not found in this session (no `code`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected, or the recipient opted out in strict mode (`RECIPIENT_OPTED_OUT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"Sending or the WhatsApp call failed (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/retry":{"post":{"tags":["messages"],"summary":"Resend an outbound message","description":"Sends a logged outbound message again as a new message with a new id. Only text, image, document, audio, video and sticker messages that still have their content can be retried. The same opt-out check as `POST /messages` applies, and the send is paced like any other.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id to resend"},"required":true,"description":"Gateway message id to resend","name":"msgId","in":"path"}],"responses":{"200":{"description":"Resend accepted","content":{"application/json":{"schema":{"type":"object","properties":{"message_id":{"type":"string","description":"Id of the new message"},"status":{"type":"string","description":"`sent`, `queued` (paced), `blocked` (recipient on the session blocklist) or `quota_exceeded` (monthly plan quota used up)"},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"description":"Present when the recipient has opted out (`RECIPIENT_OPTED_OUT`): the message was still accepted because strict mode (`enforce_api`) is off"}},"required":["message_id","status"]}}}},"400":{"description":"The message is inbound, of an unsupported type or has no content (`NOT_RETRYABLE`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected (`CONFLICT`), or the recipient opted out in strict mode (`RECIPIENT_OPTED_OUT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"Sending or the WhatsApp call failed (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/delete":{"post":{"tags":["messages"],"summary":"Delete a sent message for everyone","description":"Revokes one of your sent messages on WhatsApp and marks it `deleted` in the history. The message must already have a WhatsApp id, i.e. it was handed to WhatsApp.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id (the `id` of a message in the history)"},"required":true,"description":"Gateway message id (the `id` of a message in the history)","name":"msgId","in":"path"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found, or it has no WhatsApp id yet; unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"Sending or the WhatsApp call failed (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/edit":{"post":{"tags":["messages"],"summary":"Edit a sent message","description":"Replaces the text of one of your sent messages on WhatsApp and updates the stored body. `msgId` is the gateway message id; the message must already have a WhatsApp id.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id (the `id` of a message in the history)"},"required":true,"description":"Gateway message id (the `id` of a message in the history)","name":"msgId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"New text"}},"required":["body"]}}}},"responses":{"200":{"description":"Edited","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found, or it has no WhatsApp id yet; unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"Sending or the WhatsApp call failed (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"put":{"tags":["messages"],"summary":"Edit a message by WhatsApp id","description":"Edits a message addressed by its WhatsApp message id instead of the gateway id. Unlike `POST …/messages/{msgId}/edit` it does not look the message up and does not update the stored history.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"WhatsApp message id (`wa_message_id`), not the gateway id"},"required":true,"description":"WhatsApp message id (`wa_message_id`), not the gateway id","name":"msgId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"JID of the chat the message is in"},"new_text":{"type":"string","description":"New text"}},"required":["chat_id","new_text"]}}}},"responses":{"200":{"description":"Edited","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/react":{"post":{"tags":["messages"],"summary":"React to a message","description":"Sends an emoji reaction to a message through the normal send pipeline. `msgId` may be the gateway id or the WhatsApp id of the message.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id or WhatsApp message id"},"required":true,"description":"Gateway message id or WhatsApp message id","name":"msgId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"emoji":{"type":"string","description":"Reaction emoji; defaults to 👍 when omitted"}}}}}},"responses":{"200":{"description":"Reaction sent","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found; unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/{msgId}/read":{"post":{"tags":["messages"],"summary":"Mark a message as read","description":"Sends a read receipt for the message to WhatsApp. The local unread counter of the inbox is not changed; use `POST …/inbox/{chatId}/read` for that.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Gateway message id (the `id` of a message in the history)"},"required":true,"description":"Gateway message id (the `id` of a message in the history)","name":"msgId","in":"path"}],"responses":{"200":{"description":"Read receipt sent","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Message not found, or it has no WhatsApp id yet; unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"Sending or the WhatsApp call failed (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/messages/interactive":{"post":{"tags":["messages"],"summary":"Send a buttons or list message","description":"Sends a button or list message straight through the WhatsApp connection. Unlike `POST /messages` it is not queued or paced, not written to the message history and not checked against the opt-out list, and `chat_id` must already be a JID. A `type` other than `buttons` or `list` sends nothing but still answers 200.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Recipient JID"},"type":{"type":"string","enum":["buttons","list"]},"body":{"type":"string","description":"Message text"},"footer":{"type":"string"},"buttons":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","description":"Button label"}},"required":["text"]},"description":"For `buttons`: the buttons, in order (button ids are generated as `btn_0`, `btn_1`, …)"},"title":{"type":"string","description":"For `list`: list title"},"sections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"rowId":{"type":"string"},"description":{"type":"string"}},"required":["title","rowId"]}}},"required":["rows"]},"description":"For `list`: sections with rows, passed unchanged to the WhatsApp library. The list button is labelled `Menu`"}},"required":["chat_id","type","body"]}}}},"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: the plan's send rate per minute per API key (`send_per_minute` in `GET /v1/usage`; 30 on the Free plan), shared with every other POST whose path contains `messages`, including `POST /messages`. The response carries `Retry-After` (seconds) and `retry_after` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/auto-reply/{ruleId}":{"patch":{"tags":["auto-reply"],"summary":"Update an auto-reply rule","description":"Changes the given fields of a rule; omitted fields keep their value. The resulting regular expression is re-checked when the rule is or becomes a `regex` rule.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Rule id"},"required":true,"description":"Rule id","name":"ruleId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Rule name"},"trigger_type":{"type":"string","enum":["any","keyword","contains","regex"],"description":"`any`: every inbound message; `keyword`: the whole message equals `trigger_text`; `contains`: the message contains `trigger_text`; `regex`: the message matches the regular expression in `trigger_text` (rejected as `UNSAFE_REGEX` when it could backtrack catastrophically)"},"trigger_text":{"type":["string","null"],"maxLength":500,"description":"Text or regular expression to compare with; unused by `any`"},"match_case":{"anyOf":[{"type":"boolean"},{"type":"number","enum":[0]},{"type":"number","enum":[1]},{"type":"string","enum":["true","false","0","1"]}],"description":"Case-sensitive matching (default false)"},"reply_type":{"type":"string","enum":["text","image","document","audio","video","sticker"],"description":"Default `text`"},"reply_body":{"type":["string","null"],"maxLength":4096,"description":"Reply text; personalization tokens such as `{first_name}` or `{{first_name|there}}` are filled from the CRM"},"reply_r2_key":{"type":["string","null"],"maxLength":256,"description":"Media key from `/v1/media/upload-url` for non-text replies"},"reply_caption":{"type":["string","null"],"maxLength":1024,"description":"Caption of a media reply (tokens are filled like in `reply_body`)"},"cooldown_ms":{"type":"integer","minimum":0,"maximum":2592000000,"description":"Minimum time before this rule answers the same chat again (default 60 000)"},"priority":{"type":"integer","minimum":-10000,"maximum":10000,"description":"Rules are tried from the highest priority down; the first match answers (default 0)"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"number","enum":[0]},{"type":"number","enum":[1]},{"type":"string","enum":["true","false","0","1"]}],"description":"Inactive rules never fire (default true)"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`) or an unsafe regular expression (`UNSAFE_REGEX`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Rule not found (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["auto-reply"],"summary":"Delete an auto-reply rule","description":"Deletes the rule and its per-chat cooldown records.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Rule id"},"required":true,"description":"Rule id","name":"ruleId","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Rule not found (`NOT_FOUND`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/blocklist":{"get":{"tags":["blocklist"],"summary":"List blocked recipients","description":"Recipients this session must not send to, newest first (max 10 000). This is the gateway's own list; the list kept by WhatsApp is at `GET …/wa-blocklist`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Blocked recipients","content":{"application/json":{"schema":{"type":"object","properties":{"blocklist":{"type":"array","items":{"type":"object","properties":{"chat_id":{"type":"string","description":"JID"},"reason":{"type":["string","null"]},"added_at":{"type":"integer","description":"Time added (Unix epoch, milliseconds)"}},"required":["chat_id","reason","added_at"]}}},"required":["blocklist"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"post":{"tags":["blocklist"],"summary":"Block a recipient","description":"Adds a chat to the session blocklist (an existing entry is replaced). Sends from this session to a blocked chat are not delivered and come back with status `blocked`. This does not block the contact on WhatsApp; see `POST …/block` for that.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","minLength":1,"maxLength":64,"description":"Phone number or JID; local-format numbers are resolved against the business country"},"reason":{"type":["string","null"],"maxLength":200,"description":"Free-text note"}},"required":["chat_id"]}}}},"responses":{"200":{"description":"Blocked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"chat_id":{"type":"string","description":"The normalised JID that was stored"}},"required":["ok","chat_id"]}}}},"400":{"description":"Invalid body (`VALIDATION_FAILED`, `INVALID_JSON`) or `chat_id` is neither a phone number nor a JID (`INVALID_JID`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/blocklist/{chatId}":{"delete":{"tags":["blocklist"],"summary":"Unblock a recipient","description":"Removes a chat from the session blocklist. Answers 200 with `deleted: 0` when it was not listed.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Phone number or JID, URL-encoded; local-format numbers are resolved against the business country"},"required":true,"description":"Phone number or JID, URL-encoded; local-format numbers are resolved against the business country","name":"chatId","in":"path"}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"integer","description":"Blocklist entries removed"}},"required":["ok","deleted"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/block":{"post":{"tags":["blocklist"],"summary":"Block a contact on WhatsApp","description":"Blocks a contact in the WhatsApp account itself. This is separate from the gateway recipient blocklist.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jid":{"type":"string","description":"JID of the contact, e.g. `447700900123@s.whatsapp.net`"}},"required":["jid"]}}}},"responses":{"200":{"description":"Blocked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/unblock":{"post":{"tags":["blocklist"],"summary":"Unblock a contact on WhatsApp","description":"Removes a contact from the block list of the WhatsApp account itself.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jid":{"type":"string","description":"JID of the contact"}},"required":["jid"]}}}},"responses":{"200":{"description":"Unblocked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/wa-blocklist":{"get":{"tags":["blocklist"],"summary":"List contacts blocked on WhatsApp","description":"Fetches the block list of the WhatsApp account from WhatsApp. The gateway recipient blocklist is at `GET …/blocklist`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Blocked JIDs","content":{"application/json":{"schema":{"type":"object","properties":{"blocklist":{"type":"array","items":{"type":"string"},"description":"JIDs"}},"required":["blocklist"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/contacts":{"get":{"tags":["inbox"],"summary":"List chats seen in history","description":"Distinct chat ids found in the stored message history of the session, most recently active first (max 100). Derived from the message log, not from the WhatsApp address book.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Chats","content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"chat_id":{"type":"string"},"last_seen":{"type":"integer","description":"Time of the latest message in the chat (Unix epoch, milliseconds)"}},"required":["chat_id","last_seen"]}}},"required":["contacts"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/messages":{"get":{"tags":["inbox"],"summary":"List the messages of a chat","description":"Messages of one chat, oldest first within the page. Pass `next_cursor` as `before` to load older messages. Rows are returned as stored in the message log (more columns than listed may be present).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size; values outside 1-100 are clamped"},"required":false,"description":"Page size; values outside 1-100 are clamped","name":"limit","in":"query"},{"schema":{"type":"number","description":"`created_at` (epoch ms) of the oldest message already loaded; non-numeric values are ignored"},"required":false,"description":"`created_at` (epoch ms) of the oldest message already loaded; non-numeric values are ignored","name":"before","in":"query"}],"responses":{"200":{"description":"A page of messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageLogEntry"},"description":"Oldest first"},"has_more":{"type":"boolean","description":"`true` when the page is full"},"next_cursor":{"type":["integer","null"],"description":"`created_at` of the oldest message in the page (Unix epoch, milliseconds), or null"}},"required":["messages","has_more","next_cursor"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/close":{"post":{"tags":["inbox"],"summary":"Close a chat","description":"Marks the inbox chat `closed` and resolves its agent assignment. If the tenant enabled the CSAT survey (setting `csat_config`), the session is connected and the chat is a one-to-one chat, the survey question is sent and a pending response is recorded.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"responses":{"200":{"description":"Closed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["closed"]},"csat_sent":{"type":"boolean","description":"`true` when the CSAT survey was sent"}},"required":["ok","status","csat_sent"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/read":{"post":{"tags":["inbox"],"summary":"Reset the unread counter","description":"Sets the unread counter of the inbox chat to zero. This is local only: no read receipt is sent to WhatsApp (see `POST …/messages/{msgId}/read`). Answers 200 even when the chat is unknown.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"responses":{"200":{"description":"Reset","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/metadata":{"get":{"tags":["inbox"],"summary":"Get tags, notes, pin and archive state","description":"Returns the organisation data of a chat. A chat without any stored data returns empty tags and notes with `pinned` and `archived` false.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"responses":{"200":{"description":"Chat metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxChatMetadata"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"put":{"tags":["inbox"],"summary":"Replace tags, notes, pin and archive state","description":"Replaces the whole record: fields left out are reset (tags and notes to empty, `pinned` and `archived` to false). The body is not validated; use the dedicated tag, note, pin and archive calls to change a single thing.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"}},"required":["name","color"]}},"notes":{"type":"array","items":{"type":"string"}},"pinned":{"type":"boolean","description":"Any truthy value pins"},"archived":{"type":"boolean","description":"Any truthy value archives"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/tags":{"post":{"tags":["inbox"],"summary":"Add a tag to a chat","description":"Appends a tag to the chat. The call does not de-duplicate: adding the same name twice stores it twice.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Tag name"},"color":{"type":"string","description":"Tag colour, free text such as a hex code"}},"required":["name","color"]}}}},"responses":{"200":{"description":"Tag added","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"}},"required":["name","color"]},"description":"All tags of the chat"}},"required":["ok","tags"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/notes":{"post":{"tags":["inbox"],"summary":"Add a note to a chat","description":"Adds an internal note at the top of the chat notes. Notes are never sent to the contact.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Note text"}},"required":["text"]}}}},"responses":{"200":{"description":"Note added","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"notes":{"type":"array","items":{"type":"string"},"description":"All notes of the chat, newest first"}},"required":["ok","notes"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/pin":{"post":{"tags":["inbox"],"summary":"Toggle the pin of a chat","description":"Flips the pinned flag of the chat (pinned becomes unpinned and the other way round). The response does not return the new state; read it from the metadata.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"responses":{"200":{"description":"Toggled","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/archive":{"post":{"tags":["inbox"],"summary":"Toggle the archive flag of a chat","description":"Flips the archived flag of the chat. The response does not return the new state; read it from the metadata.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"responses":{"200":{"description":"Toggled","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/inbox/{chatId}/disappearing":{"post":{"tags":["inbox"],"summary":"Set local message expiry for a chat","description":"Stores a retention time for the chat. A background job that runs every 5 minutes deletes the messages of the chat older than that time from the stored history. It does not change the disappearing-messages setting of the chat on WhatsApp (for groups see `PUT …/groups/{gid}/ephemeral`). `null` or 0 turns it off.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied"},"required":true,"description":"Chat id (JID) exactly as stored in the inbox, e.g. `447700900123@s.whatsapp.net` or `…@g.us`. URL-decoded by the server; no phone-number normalisation is applied","name":"chatId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"duration_ms":{"type":["integer","null"],"description":"Keep messages this long, in milliseconds; `null` or 0 disables"}},"required":["duration_ms"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"duration_ms":{"type":["integer","null"]}},"required":["ok","duration_ms"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/scheduled":{"get":{"tags":["scheduled"],"summary":"List scheduled messages","description":"All scheduled messages of the session, sent or not, ordered by due time (oldest first).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Scheduled messages","content":{"application/json":{"schema":{"type":"object","properties":{"scheduled":{"type":"array","items":{"$ref":"#/components/schemas/SessionScheduledMessage"}}},"required":["scheduled"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"post":{"tags":["scheduled"],"summary":"Schedule a message","description":"Queues a message for later delivery. A worker checks every 10 seconds and sends due messages once the session is connected; the bulk send window does not apply. A recipient who opted out before delivery makes the message `failed` with error `opted_out`. The body is not validated: `chat_id` is stored as given and must be a full JID, and a missing `scheduled_at` ends in a 500. Limited to 20 calls per minute per key.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Recipient JID, e.g. `447700900123@s.whatsapp.net`; phone numbers are not normalised"},"message_type":{"type":"string","description":"Default `text`"},"body":{"type":"string","description":"Message text"},"media_r2_key":{"type":"string","description":"Media key from `/v1/media/upload-url`"},"caption":{"type":"string"},"scheduled_at":{"type":"integer","description":"Delivery time (Unix epoch, milliseconds)"}},"required":["chat_id","scheduled_at"]}}}},"responses":{"201":{"description":"Scheduled","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]}},"required":["id","status"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Strict rate limit exceeded: 20 requests per minute, shared with `POST /v1/scheduled` (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/scheduled/{msgId}":{"delete":{"tags":["scheduled"],"summary":"Cancel a scheduled message","description":"Deletes a scheduled message that has not been sent yet. Messages that are already `sending`, `sent` or `failed` cannot be removed.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Scheduled message id (`id` from the list)"},"required":true,"description":"Scheduled message id (`id` from the list)","name":"msgId","in":"path"}],"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Not found, not yours, or no longer `pending` (no `code`); unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/status":{"post":{"tags":["status"],"summary":"Post a Status update","description":"Posts a text or media Status update through the normal send pipeline (pacing and monthly quota apply). The response only confirms that the request was handed over, not the delivery result.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","description":"Message type, default `text` (`image`, `video`, … for media)"},"body":{"type":"string","description":"Text of a text Status"},"media_r2_key":{"type":"string","description":"Media key from `/v1/media/upload-url`"},"caption":{"type":"string"},"jid_list":{"type":"array","items":{"type":"string"},"description":"JIDs the Status is delivered to"}}}}}},"responses":{"200":{"description":"Handed over","content":{"application/json":{"schema":{"type":"object","properties":{"message_id":{"type":"string"},"ok":{"type":"boolean"}},"required":["message_id","ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/status/text":{"post":{"tags":["status"],"summary":"Post a text Status","description":"Posts a text Status straight through the WhatsApp connection (not queued, not logged in the message history).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Status text"},"background_color":{"type":"string","description":"Background colour, passed unchanged to the WhatsApp library"}},"required":["text"]}}}},"responses":{"200":{"description":"Posted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/status/image":{"post":{"tags":["status"],"summary":"Post an image Status","description":"Downloads an image from a public URL (max 5 MB, 15-second limit, private addresses refused) and posts it as a Status straight through the WhatsApp connection (not queued, not logged in the message history).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image_url":{"type":"string","description":"Public http(s) URL of the image"},"caption":{"type":"string"}},"required":["image_url"]}}}},"responses":{"200":{"description":"Posted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON, or `image_url` is missing, is not an http(s) URL, answers with an HTTP error, is larger than 5 MB, or points at a private/local address (`error` starts with `image_url`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups":{"get":{"tags":["groups"],"summary":"List groups","description":"Groups the account participates in, sorted by name. The list is empty when the session has no live connection or WhatsApp does not answer within 8 seconds (such failures are not reported as errors).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Groups","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"jid":{"type":"string"},"name":{"type":"string"},"size":{"type":"integer","description":"Number of participants"},"desc":{"type":["string","null"],"description":"Group description"}},"required":["jid","name","size","desc"]}}},"required":["groups"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/create":{"post":{"tags":["groups"],"summary":"Create a group","description":"Creates a WhatsApp group with the given members.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","description":"Group name"},"participants":{"type":"array","items":{"type":"string"},"description":"JIDs of the initial members, `<digits>@s.whatsapp.net`"}},"required":["subject","participants"]}}}},"responses":{"200":{"description":"Group created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaGroupMetadata"}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/metadata":{"get":{"tags":["groups"],"summary":"Get group details","description":"Returns the metadata WhatsApp holds for the group, including its participants.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"responses":{"200":{"description":"Group metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaGroupMetadata"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/subject":{"put":{"tags":["groups"],"summary":"Rename a group","description":"Changes the group name.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","description":"New group name"}},"required":["subject"]}}}},"responses":{"200":{"description":"Renamed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/description":{"put":{"tags":["groups"],"summary":"Change a group description","description":"Changes the group description.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"New description"}},"required":["description"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/ephemeral":{"put":{"tags":["groups"],"summary":"Set group disappearing messages","description":"Turns the disappearing-messages timer of the group on or off on WhatsApp.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expiration":{"type":"integer","description":"Timer in seconds: 0 turns it off; WhatsApp uses 86400 (24 hours), 604800 (7 days) and 7776000 (90 days)"}},"required":["expiration"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/invite":{"get":{"tags":["groups"],"summary":"Get the group invite link","description":"Returns the current invite link of the group.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"responses":{"200":{"description":"Invite link","content":{"application/json":{"schema":{"type":"object","properties":{"invite_link":{"type":"string","description":"`https://chat.whatsapp.com/<code>`"}},"required":["invite_link"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/leave":{"post":{"tags":["groups"],"summary":"Leave a group","description":"Makes the account leave the group.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"responses":{"200":{"description":"Left","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/participants/add":{"post":{"tags":["groups"],"summary":"Add group members","description":"Adds members to the group. The per-member outcome returned by WhatsApp is passed through in `result`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"string"},"description":"Member JIDs, `<digits>@s.whatsapp.net`"}},"required":["participants"]}}}},"responses":{"200":{"description":"Outcome per member","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","description":"Status code per member as returned by WhatsApp"},"jid":{"type":"string"}},"required":["status"]}}},"required":["result"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/participants/remove":{"post":{"tags":["groups"],"summary":"Remove group members","description":"Removes members from the group. The per-member outcome returned by WhatsApp is passed through in `result`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"string"},"description":"Member JIDs, `<digits>@s.whatsapp.net`"}},"required":["participants"]}}}},"responses":{"200":{"description":"Outcome per member","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","description":"Status code per member as returned by WhatsApp"},"jid":{"type":"string"}},"required":["status"]}}},"required":["result"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/participants/promote":{"post":{"tags":["groups"],"summary":"Make members group admins","description":"Promotes members to group admin.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"string"},"description":"Member JIDs, `<digits>@s.whatsapp.net`"}},"required":["participants"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/participants/demote":{"post":{"tags":["groups"],"summary":"Remove group admin rights","description":"Demotes group admins to regular members.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"string"},"description":"Member JIDs, `<digits>@s.whatsapp.net`"}},"required":["participants"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/groups/{gid}/participants":{"post":{"tags":["groups"],"summary":"Change group members","description":"Adds, removes, promotes or demotes members with one endpoint. The same actions exist as separate `…/participants/add|remove|promote|demote` calls; the add and remove variants return the per-member `result`, this one only `ok`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Group JID, `<id>@g.us`"},"required":true,"description":"Group JID, `<id>@g.us`","name":"gid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["add","remove","promote","demote"]},"participants":{"type":"array","items":{"type":"string"},"description":"Member JIDs, `<digits>@s.whatsapp.net`"}},"required":["action","participants"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"WhatsApp refused the call, for example because of an invalid `action` (`code` is usually `INTERNAL`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/communities/create":{"post":{"tags":["communities"],"summary":"Create a community","description":"Creates a WhatsApp community. WhatsApp may answer with no group data, in which case the response body is `null`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","description":"Community name"},"body":{"type":"string","description":"Community description"}},"required":["subject","body"]}}}},"responses":{"200":{"description":"Community created","content":{"application/json":{"schema":{"type":["object","null"],"properties":{"id":{"type":"string","description":"Group JID"},"subject":{"type":"string","description":"Group name"},"owner":{"type":"string"},"creation":{"type":"integer","description":"Creation time as reported by WhatsApp (Unix epoch)"},"desc":{"type":"string","description":"Group description"},"size":{"type":"integer"},"restrict":{"type":"boolean","description":"Only admins can change group settings"},"announce":{"type":"boolean","description":"Only admins can write"},"ephemeralDuration":{"type":"integer","description":"Disappearing-messages timer in seconds"},"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"admin":{"type":["string","null"],"description":"`admin`, `superadmin` or null"}},"required":["id"]}}},"required":["id","subject","participants"],"description":"Metadata of the new community group, or null"}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/communities/{cid}/link-group":{"post":{"tags":["communities"],"summary":"Link a group to a community","description":"Not available yet: the WhatsApp library version in use (Baileys 6.7.23) has no `communityLinkGroup` method, so this answers 501 `NOT_SUPPORTED`. The request shape is the one the handler reads.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Community JID"},"required":true,"description":"Community JID","name":"cid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"group_jid":{"type":"string","description":"JID of the group to link"}},"required":["group_jid"]}}}},"responses":{"200":{"description":"Linked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"501":{"description":"`NOT_SUPPORTED`: the WhatsApp library version in use (Baileys 6.7.x) has no such operation, so the call is not available yet","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/newsletter/create":{"post":{"tags":["newsletters"],"summary":"Create a channel","description":"Creates a WhatsApp channel (newsletter).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Channel name"},"description":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Channel created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Channel JID, `<id>@newsletter`"},"name":{"type":"string"},"description":{"type":"string"},"invite":{"type":"string"},"creation_time":{"type":"integer"},"subscribers":{"type":"integer"}},"required":["id","name"],"description":"Channel metadata as returned by WhatsApp; further fields may be present"}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/newsletter/{nid}/post":{"post":{"tags":["newsletters"],"summary":"Post to a channel","description":"Sends a text post to a channel straight through the WhatsApp connection (not queued, not logged in the message history).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Channel JID, `<id>@newsletter`"},"required":true,"description":"Channel JID, `<id>@newsletter`","name":"nid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Post text"}},"required":["text"]}}}},"responses":{"200":{"description":"Posted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/presence":{"post":{"tags":["whatsapp-account"],"summary":"Send a presence update","description":"Sets the presence of the account, or shows \"typing\" or \"recording\" in a chat.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["available","unavailable","composing","recording","paused"]},"jid":{"type":"string","description":"Chat the update is for (typing and recording indicators)"}},"required":["type"]}}}},"responses":{"200":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/privacy":{"get":{"tags":["whatsapp-account"],"summary":"Get WhatsApp privacy settings","description":"Fetches the privacy settings of the account from WhatsApp. The result is a map of setting name to value exactly as WhatsApp returns it. For data-subject export and erasure see the `privacy` tag.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Privacy settings","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"description":"Setting name → value"}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/privacy/read-receipts":{"put":{"tags":["whatsapp-account"],"summary":"Set read receipts privacy","description":"Turns read receipts on or off for the account.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","enum":["all","none"],"description":"`all` sends read receipts, `none` does not"}},"required":["value"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/privacy/online":{"put":{"tags":["whatsapp-account"],"summary":"Set online visibility privacy","description":"Controls who can see when the account is online.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","enum":["all","match_last_seen"],"description":"`all`: everyone; `match_last_seen`: same audience as last seen"}},"required":["value"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/profile/picture":{"put":{"tags":["whatsapp-account"],"summary":"Set a profile picture","description":"Downloads an image from a public URL (max 5 MB, 15-second limit, private addresses refused) and sets it as the picture of the account or of a group.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jid":{"type":"string","description":"JID whose picture is set: the account itself or a group"},"image_url":{"type":"string","description":"Public http(s) URL of the image"}},"required":["jid","image_url"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON, or `image_url` is missing, is not an http(s) URL, answers with an HTTP error, is larger than 5 MB, or points at a private/local address (`error` starts with `image_url`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/profile/status":{"put":{"tags":["whatsapp-account"],"summary":"Set the About text","description":"Changes the About (status text) of the account.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"New About text"}},"required":["status"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/calls/auto-reject":{"post":{"tags":["whatsapp-account"],"summary":"Turn call auto-reject on or off","description":"When on, incoming WhatsApp call offers on this session are rejected automatically. The setting is stored without contacting WhatsApp, so it also works while the session is offline; only the value `true` turns it on.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"auto_reject":{"type":"boolean","description":"The submitted value"}},"required":["ok","auto_reject"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/business/profile":{"put":{"tags":["whatsapp-business"],"summary":"Update the business profile","description":"Not available yet: the WhatsApp library version in use (Baileys 6.7.23) has no `updateBussinesProfile` method, so this answers 501 `NOT_SUPPORTED`. The handler would pass the JSON body to the library unchanged and does not validate it.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"description":"Business profile fields, passed unchanged to the WhatsApp library"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"501":{"description":"`NOT_SUPPORTED`: the WhatsApp library version in use (Baileys 6.7.x) has no such operation, so the call is not available yet","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/business/catalog":{"get":{"tags":["whatsapp-business"],"summary":"List catalog products","description":"Fetches up to 100 products of a business catalog from WhatsApp. There is no paging parameter, so `nextPageCursor` cannot be followed.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Business whose catalog to read; defaults to the account of the session"},"required":false,"description":"Business whose catalog to read; defaults to the account of the session","name":"jid","in":"query"}],"responses":{"200":{"description":"Catalog","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/WaCatalogProduct"}},"nextPageCursor":{"type":"string"}},"required":["products"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/business/catalog/products":{"post":{"tags":["whatsapp-business"],"summary":"Create a catalog product","description":"Creates a product in the catalog of the account. Each image is downloaded by the gateway through the outbound-URL guard (http/https only, max 5 MB, no private or local addresses) and uploaded as bytes; anything else in `images` is a 400. The remaining fields are passed unchanged to the WhatsApp library and not validated by the gateway; they follow the library's product type.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Product name"},"description":{"type":"string","description":"Product description"},"price":{"type":"number","description":"Price as the WhatsApp library expects it"},"currency":{"type":"string","description":"Currency code"},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"Public http(s) URL of the image (max 5 MB). The gateway downloads it itself; private and local addresses are refused"}},"required":["url"]},"maxItems":10,"description":"Product images, each given as `{ \"url\": \"https://…\" }` (at most 10)"},"retailerId":{"type":"string","description":"Your own product id"},"url":{"type":"string","description":"Link to the product page"},"isHidden":{"type":"boolean"},"originCountryCode":{"type":"string","description":"ISO country code of origin"}},"required":["name","description","price","currency","images"]}}}},"responses":{"200":{"description":"Product created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaCatalogProduct"}}}},"400":{"description":"The body is not valid JSON, or `image_url` is missing, is not an http(s) URL, answers with an HTTP error, is larger than 5 MB, or points at a private/local address (`error` starts with `image_url`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["whatsapp-business"],"summary":"Delete catalog products","description":"Deletes products from the catalog; the ids travel in the JSON body of this DELETE request.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_ids":{"type":"array","items":{"type":"string"},"description":"Product ids"}},"required":["product_ids"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"Products deleted"}},"required":["deleted"]}}}},"400":{"description":"The body is not valid JSON","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/business/catalog/products/{pid}":{"put":{"tags":["whatsapp-business"],"summary":"Update a catalog product","description":"Updates a catalog product. Images are handled as in `POST …/business/catalog/products` (downloaded by the gateway, never fetched from a URL by the library). The remaining fields are passed unchanged to the WhatsApp library and not validated by the gateway.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"},{"schema":{"type":"string","description":"Product id"},"required":true,"description":"Product id","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Product name"},"description":{"type":"string","description":"Product description"},"price":{"type":"number","description":"Price as the WhatsApp library expects it"},"currency":{"type":"string","description":"Currency code"},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"Public http(s) URL of the image (max 5 MB). The gateway downloads it itself; private and local addresses are refused"}},"required":["url"]},"maxItems":10,"description":"Product images, each given as `{ \"url\": \"https://…\" }` (at most 10)"},"retailerId":{"type":"string","description":"Your own product id"},"url":{"type":"string","description":"Link to the product page"},"isHidden":{"type":"boolean"}},"required":["name","description","price","currency","images"]}}}},"responses":{"200":{"description":"Product updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaCatalogProduct"}}}},"400":{"description":"The body is not valid JSON, or `image_url` is missing, is not an http(s) URL, answers with an HTTP error, is larger than 5 MB, or points at a private/local address (`error` starts with `image_url`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions, or the operation is not available to CS-agent logins","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/sessions/{sessionId}/labels":{"get":{"tags":["whatsapp-business"],"summary":"List WhatsApp Business labels","description":"Not available yet: the WhatsApp library version in use (Baileys 6.7.23) has no `getLabels` method, so this answers 501 `NOT_SUPPORTED`. The handler would return `{ \"labels\": [...] }`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Session (device) id"},"required":true,"description":"Session (device) id","name":"sessionId","in":"path"}],"responses":{"200":{"description":"Labels","content":{"application/json":{"schema":{"type":"object","properties":{"labels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["labels"]}}}},"401":{"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The API key is restricted to other sessions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Unknown session, or a session of another tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The session is not connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"`WhatsApp operation failed`: WhatsApp or the library rejected the call. The gateway does not validate this body, so missing or malformed fields end up here too","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"501":{"description":"`NOT_SUPPORTED`: the WhatsApp library version in use (Baileys 6.7.x) has no such operation, so the call is not available yet","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/api-keys":{"get":{"tags":["api-keys"],"summary":"List API keys","description":"Lists the tenant's primary key and its child keys (primary first, then newest first, max 1 000), revoked ones included. Only the 8-character `prefix` is returned, never the key itself. Readable with an API key as well as from a dashboard login.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"api_keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","description":"First 8 characters of the key, safe to display. The full key is only returned once, when it is created"},"is_primary":{"type":"boolean","description":"`true` for the tenant's own key, `false` for a child key created with `POST /v1/api-keys`"},"rate_limit_per_minute":{"type":"integer"},"created_at":{"type":"integer","description":"Unix time in milliseconds"},"last_used_at":{"type":["integer","null"],"description":"Unix time in milliseconds of the last authenticated request (updated at most once a minute), or null if never used"},"revoked_at":{"type":["integer","null"],"description":"When the key was revoked, or null while it is active"}},"required":["id","name","prefix","is_primary","rate_limit_per_minute","created_at","last_used_at","revoked_at"]}}},"required":["api_keys"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["api-keys"],"summary":"Create a child API key","description":"Creates a child key that acts as the tenant (same devices, quota, plan and settings) but has its own per-minute request limit and can be revoked on its own. The full key is returned once, in this response only. Needs a dashboard login: an API key cannot mint keys. With the platform admin secret the new key is a separate standalone tenant on the Free plan instead.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"Label, 1-64 characters (default `unnamed`)"},"rate_limit_per_minute":{"type":"integer","minimum":1,"maximum":10000,"description":"Requests per minute this key may make, across all `/v1` endpoints (1-10 000, default 300). Capped to the plan ceiling: 5 times the plan's send rate per minute, at least 300 (600 on Pro with the default send rates)"}}}}}},"responses":{"201":{"description":"Key created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string","description":"The secret API key. Shown only in this response; store it securely"},"prefix":{"type":"string","description":"First 8 characters of the key"},"name":{"type":"string"}},"required":["id","key","prefix","name"]}}}},"400":{"description":"Body is not valid JSON (`INVALID_JSON`) or fails validation (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden. `KEY_MANAGEMENT_REQUIRES_LOGIN` when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with `KEY_SCOPE_FORBIDDEN` for a session-restricted key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/api-keys/{id}":{"patch":{"tags":["api-keys"],"summary":"Rename a key or change its rate limit","description":"Updates the name and/or the per-minute rate limit of the tenant's primary key or one of its child keys. The rate limit is the number of requests per minute the key may make with its API key; it is capped to the plan ceiling (5 times the plan's send rate per minute, at least 300), the response returns the value that was applied, and a change takes effect on the next request. Needs a dashboard login.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"API key id"},"required":true,"description":"API key id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"Label, 1-64 characters"},"rate_limit_per_minute":{"type":"integer","minimum":1,"maximum":10000,"description":"Requests per minute this key may make, across all `/v1` endpoints (1-10 000, default 300). Capped to the plan ceiling: 5 times the plan's send rate per minute, at least 300 (600 on Pro with the default send rates)"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"rate_limit_per_minute":{"type":"integer","description":"Rate limit now in force (after the plan cap)"}},"required":["ok","rate_limit_per_minute"]}}}},"400":{"description":"Body is not valid JSON (`INVALID_JSON`) or fails validation (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden. `KEY_MANAGEMENT_REQUIRES_LOGIN` when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with `KEY_SCOPE_FORBIDDEN` for a session-restricted key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"API key not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["api-keys"],"summary":"Revoke a child API key (alias)","description":"Alias of `POST /v1/api-keys/{id}/revoke`: the key is revoked, not removed, and stays in the list with `revoked_at` set. The primary key cannot be revoked. Needs a dashboard login.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"API key id"},"required":true,"description":"API key id","name":"id","in":"path"}],"responses":{"200":{"description":"Revoked (also returned when the key was already revoked)","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The tenant's own primary key cannot be revoked, because it would lock the tenant out of its dashboard (`CANNOT_REVOKE_PRIMARY`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden. `KEY_MANAGEMENT_REQUIRES_LOGIN` when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with `KEY_SCOPE_FORBIDDEN` for a session-restricted key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"API key not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/api-keys/{id}/revoke":{"post":{"tags":["api-keys"],"summary":"Revoke a child API key","description":"Revokes one of the tenant's child keys: it stops authenticating immediately. The key stays in the list with `revoked_at` set. The primary key cannot be revoked. Same effect as `DELETE /v1/api-keys/{id}`. Needs a dashboard login.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"API key id"},"required":true,"description":"API key id","name":"id","in":"path"}],"responses":{"200":{"description":"Revoked (also returned when the key was already revoked)","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The tenant's own primary key cannot be revoked, because it would lock the tenant out of its dashboard (`CANNOT_REVOKE_PRIMARY`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden. `KEY_MANAGEMENT_REQUIRES_LOGIN` when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with `KEY_SCOPE_FORBIDDEN` for a session-restricted key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"API key not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/templates":{"get":{"tags":["templates"],"summary":"List message templates","description":"All templates of the tenant, newest first.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"name":{"type":"string"},"message_type":{"type":"string","description":"`text`, `image`, `document`, `audio` or `video`"},"body":{"type":["string","null"]},"media_r2_key":{"type":["string","null"],"description":"Key of an uploaded media file (see `/v1/media`)"},"caption":{"type":["string","null"]},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","api_key_id","name","message_type","body","media_r2_key","caption","created_at"]}}},"required":["templates"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["templates"],"summary":"Create a message template","description":"Saves a template. Only `name` is required; `message_type` defaults to `text`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"message_type":{"type":"string","enum":["text","image","document","audio","video"],"default":"text"},"body":{"type":["string","null"],"maxLength":4096,"description":"Message text; may contain the personalization tokens used by blasts"},"media_r2_key":{"type":["string","null"],"maxLength":256,"description":"Key of an uploaded media file (see `/v1/media`)"},"caption":{"type":["string","null"],"maxLength":1024}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"description":"Body is not valid JSON (`INVALID_JSON`) or fails validation (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/templates/{id}":{"patch":{"tags":["templates"],"summary":"Edit a message template","description":"Edits a template in place (id and `created_at` are kept). Send only the fields to change; `null` clears `body`, `media_r2_key` and `caption`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Template id"},"required":true,"description":"Template id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"message_type":{"type":"string","enum":["text","image","document","audio","video"]},"body":{"type":["string","null"],"maxLength":4096,"description":"Message text; may contain the personalization tokens used by blasts"},"media_r2_key":{"type":["string","null"],"maxLength":256,"description":"Key of an uploaded media file (see `/v1/media`)"},"caption":{"type":["string","null"],"maxLength":1024}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Body is not valid JSON (`INVALID_JSON`) or fails validation (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Template not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["templates"],"summary":"Delete a message template","description":"Permanently deletes a template.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Template id"},"required":true,"description":"Template id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Template not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/webhooks/deliveries":{"get":{"tags":["webhooks"],"summary":"List webhook deliveries","description":"Delivery log, newest first: one row per event and webhook, updated on every attempt. Use it to see why an endpoint is not receiving events.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Rows to return; values outside 1-200 are clamped"},"required":false,"description":"Rows to return; values outside 1-200 are clamped","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":64,"description":"Only deliveries of this webhook"},"required":false,"description":"Only deliveries of this webhook","name":"webhook_id","in":"query"}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"webhook_id":{"type":"string"},"url":{"type":"string","description":"Endpoint URL at the time of delivery"},"event":{"type":"string","description":"Event type, e.g. `message.received` or `webhook.test`"},"status":{"type":"string","enum":["delivered","failed","retrying"],"description":"`retrying` while attempts remain, `failed` once they are exhausted"},"attempts":{"type":"integer"},"response_status":{"type":["integer","null"],"description":"HTTP status returned by the endpoint on the last attempt, or null if it never answered"},"error":{"type":["string","null"],"description":"Short reason for the last failure: `HTTP <status>`, `timeout`, `blocked_destination`, `dns_error`, `connection_refused` or `network_error`"},"created_at":{"type":"integer","description":"Unix time in milliseconds"},"delivered_at":{"type":["integer","null"],"description":"When the delivery succeeded, or null"}},"required":["id","webhook_id","url","event","status","attempts","response_status","error","created_at","delivered_at"]}}},"required":["deliveries"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/webhooks/{id}":{"patch":{"tags":["webhooks"],"summary":"Update a webhook","description":"Changes the URL, secret, event list and/or the paused state of a webhook. A new URL goes through the same HTTPS and private-address checks as on creation. Send only the fields to change.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Webhook id"},"required":true,"description":"Webhook id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":512,"format":"uri"},"secret":{"type":"string","minLength":32,"maxLength":256,"description":"New signing secret, at least 32 characters. Write-only: it is never returned"},"events":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":50,"description":"Event types to deliver (1-50 entries, each up to 64 characters)"},"disabled":{"type":"boolean","description":"`true` pauses deliveries, `false` resumes them"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Validation failed (`VALIDATION_FAILED`, `INVALID_JSON`) or the URL points at a blocked destination (`WEBHOOK_URL_BLOCKED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Webhook not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["webhooks"],"summary":"Delete a webhook","description":"Permanently deletes a webhook; no further events are delivered to it.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Webhook id"},"required":true,"description":"Webhook id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Webhook not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/webhooks/{id}/test":{"post":{"tags":["webhooks"],"summary":"Send a test event to a webhook","description":"Sends a signed `webhook.test` event to the webhook URL (10 second timeout) and reports the outcome. The attempt is recorded in the delivery log and is never retried. An unreachable or failing endpoint still returns HTTP 200 with `ok: false`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Webhook id"},"required":true,"description":"Webhook id","name":"id","in":"path"}],"responses":{"200":{"description":"Outcome of the delivery attempt","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true` when the endpoint answered with a 2xx status"},"status":{"type":["integer","null"],"description":"HTTP status returned by the endpoint, or null when it could not be reached"},"message":{"type":"string","description":"Human-readable result, e.g. `Delivered` or `Endpoint responded HTTP 500`"}},"required":["ok","status","message"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Webhook not found, or it belongs to another tenant (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/recurring":{"get":{"tags":["recurring"],"summary":"List recurring messages","description":"Recurring messages of the tenant, newest first (max 1 000).","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Recurring messages","content":{"application/json":{"schema":{"type":"object","properties":{"recurring":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string","description":"Device the message is sent from"},"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"},"description":"Recipient JIDs (phone numbers are normalised to JIDs when saved)"},"message":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}}},"timezone":{"type":"string","description":"IANA timezone the schedule is evaluated in"},"next_run_at":{"type":["integer","null"],"description":"Next planned run, Unix ms"},"last_run_at":{"type":["integer","null"],"description":"Last run, Unix ms"},"is_active":{"type":"boolean"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","name","targets","message","media_r2_key","schedule_type","schedule_config","timezone","next_run_at","last_run_at","is_active","created_at"]}}},"required":["recurring"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["recurring"],"summary":"Create a recurring message","description":"Schedules a message to be sent to a list of recipients every day, week, month or N days. `next_run_at` is computed by the server in the schedule timezone. These sends are not held back by the blast send window.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"default":"Untitled","description":"Label, 1-100 characters"},"session_id":{"type":"string","minLength":1,"maxLength":64,"description":"Device to send from; must belong to the tenant"},"targets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":1000,"description":"Phone numbers (international format; local numbers use the business country) or JIDs, 1-1 000. Duplicates are removed; any invalid entry rejects the request with `INVALID_TARGETS`"},"message":{"type":["string","null"],"maxLength":4096,"description":"Text to send (max 4 096 characters). Either `message` or `media_r2_key` is required"},"media_r2_key":{"type":["string","null"],"maxLength":256,"description":"Key of an uploaded media file to send"},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}},"default":{}},"timezone":{"type":"string","minLength":1,"maxLength":64,"description":"IANA timezone (default: the business timezone from the profile, else `Asia/Jakarta`)"},"is_active":{"type":"boolean","description":"Default `true`. Also accepts 1/0 and the strings `true`/`false`/`1`/`0`"}},"required":["session_id","targets","schedule_type"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string","description":"Device the message is sent from"},"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"},"description":"Recipient JIDs (phone numbers are normalised to JIDs when saved)"},"message":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}}},"timezone":{"type":"string","description":"IANA timezone the schedule is evaluated in"},"next_run_at":{"type":["integer","null"],"description":"Next planned run, Unix ms"},"last_run_at":{"type":["integer","null"],"description":"Last run, Unix ms"},"is_active":{"type":"boolean"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","name","targets","message","media_r2_key","schedule_type","schedule_config","timezone","next_run_at","last_run_at","is_active","created_at"]}}}},"400":{"description":"`VALIDATION_FAILED` (bad body, unknown timezone, `schedule_config.day` out of range, or neither `message` nor `media_r2_key`) or `INVALID_TARGETS`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Session not found or not yours (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/recurring/{id}":{"patch":{"tags":["recurring"],"summary":"Edit a recurring message","description":"Changes any of the fields given at creation (send only those to change). `next_run_at` is recomputed on every edit. The message must still have a `message` or a `media_r2_key` afterwards. Use `is_active: false` to pause.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Recurring message id"},"required":true,"description":"Recurring message id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Label, 1-100 characters"},"session_id":{"type":"string","minLength":1,"maxLength":64,"description":"Device to send from; must belong to the tenant"},"targets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":1000,"description":"Phone numbers (international format; local numbers use the business country) or JIDs, 1-1 000. Duplicates are removed; any invalid entry rejects the request with `INVALID_TARGETS`"},"message":{"type":["string","null"],"maxLength":4096,"description":"Text to send (max 4 096 characters). Either `message` or `media_r2_key` is required"},"media_r2_key":{"type":["string","null"],"maxLength":256,"description":"Key of an uploaded media file to send"},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}}},"timezone":{"type":"string","minLength":1,"maxLength":64,"description":"IANA timezone (default: the business timezone from the profile, else `Asia/Jakarta`)"},"is_active":{"type":"boolean","description":"Also accepts 1/0 and the strings `true`/`false`/`1`/`0`"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"recurring":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string","description":"Device the message is sent from"},"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"},"description":"Recipient JIDs (phone numbers are normalised to JIDs when saved)"},"message":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"schedule_type":{"type":"string","enum":["daily","weekly","monthly","interval"]},"schedule_config":{"type":"object","properties":{"hour":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Hour of day in the schedule timezone (default 9)"},"minute":{"type":["integer","null"],"minimum":0,"maximum":59,"description":"Minute (default 0)"},"day":{"type":["integer","null"],"minimum":0,"maximum":31,"description":"`weekly`: weekday 0-6 with 0 = Sunday (default 1). `monthly`: day of month 1-31, clamped to the month's last day (default 1)"},"every_days":{"type":"integer","minimum":1,"maximum":365,"description":"`interval`: run every N days (default 1)"}}},"timezone":{"type":"string","description":"IANA timezone the schedule is evaluated in"},"next_run_at":{"type":["integer","null"],"description":"Next planned run, Unix ms"},"last_run_at":{"type":["integer","null"],"description":"Last run, Unix ms"},"is_active":{"type":"boolean"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","name","targets","message","media_r2_key","schedule_type","schedule_config","timezone","next_run_at","last_run_at","is_active","created_at"]}},"required":["ok","recurring"]}}}},"400":{"description":"`VALIDATION_FAILED` (bad body, unknown timezone, `schedule_config.day` out of range, or neither `message` nor `media_r2_key`) or `INVALID_TARGETS`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Recurring message not found, or the new `session_id` is not yours (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["recurring"],"summary":"Delete a recurring message","description":"Permanently deletes a recurring message.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Recurring message id"},"required":true,"description":"Recurring message id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Recurring message not found (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/rotators":{"get":{"tags":["rotators"],"summary":"List rotators","description":"Rotators of the tenant, newest first.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Rotators","content":{"application/json":{"schema":{"type":"object","properties":{"rotators":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"name":{"type":"string"},"session_ids":{"type":"array","items":{"type":"string"},"description":"Devices in the pool"},"strategy":{"type":"string","description":"`random` (default) or `round-robin`; any other stored value behaves like `random`"},"last_used_idx":{"type":["integer","null"],"description":"Round-robin position"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","api_key_id","name","session_ids","strategy","last_used_idx","created_at"]}}},"required":["rotators"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["rotators"],"summary":"Create a rotator","description":"Creates a pool of devices that outgoing messages are spread over (see `POST /v1/rotators/{id}/send`). The handler does not validate `name`, but it is required.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Label"},"session_ids":{"type":"array","items":{"type":"string"},"description":"Device ids in the pool (default: empty); every one must belong to the tenant"},"strategy":{"type":"string","description":"`random` (default) or `round-robin`. Not validated: any other value behaves like `random`"}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"description":"`session_ids` must be an array of the tenant's own device ids (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/rotators/{id}":{"patch":{"tags":["rotators"],"summary":"Edit a rotator","description":"Changes the name, device list and/or strategy. Send only the fields to change; an empty `name` or `strategy` is ignored.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Rotator id"},"required":true,"description":"Rotator id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"session_ids":{"type":"array","items":{"type":"string"},"description":"Device ids; every one must belong to the tenant"},"strategy":{"type":"string","description":"`random` (default) or `round-robin`. Not validated: any other value behaves like `random`"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"`session_ids` must be an array of the tenant's own device ids (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Rotator not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["rotators"],"summary":"Delete a rotator","description":"Permanently deletes a rotator. The devices in it are not affected.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Rotator id"},"required":true,"description":"Rotator id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Rotator not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/rotators/{id}/send":{"post":{"tags":["rotators"],"summary":"Send through a rotator","description":"Sends one message through one of the rotator's connected devices, picked by its strategy (round-robin or random). It is a 1:1 send like `POST /v1/sessions/{id}/messages`: an opted-out recipient still receives it with a `RECIPIENT_OPTED_OUT` warning, or is refused with 409 when the tenant enabled strict consent (`enforce_api`). The body fields are passed to the device as given and are not validated here beyond `chat_id`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Rotator id"},"required":true,"description":"Rotator id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Recipient JID, or a phone number (international format; local numbers use the business country or `country_code`)"},"country_code":{"type":"string","description":"Calling code used only for a local-format number"},"type":{"type":"string","description":"Message type as in `POST /v1/sessions/{sessionId}/messages` (`text` by default)"},"body":{"type":"string","description":"Text content"},"media_r2_key":{"type":"string","description":"Key of an uploaded media file"},"caption":{"type":"string"},"location":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"]},"poll_name":{"type":"string"},"choices":{"type":"array","items":{"type":"string"}},"reply_to":{"type":"string","description":"Id of the message to quote"}},"required":["chat_id"]}}}},"responses":{"202":{"description":"Accepted for sending","content":{"application/json":{"schema":{"type":"object","properties":{"message_id":{"type":"string","description":"Id of the logged message"},"session_used":{"type":"string","description":"Device the message was sent from"},"status":{"type":"string","description":"Send status reported by the device, e.g. `queued` or `sent`"},"queued_position":{"type":"integer"},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"description":"`RECIPIENT_OPTED_OUT` when the recipient has opted out"}},"required":["message_id","session_used","status"]}}}},"400":{"description":"`chat_id` missing (no `code`), `INVALID_NUMBER` or `INVALID_JID`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Rotator not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"None of the rotator's devices is connected (no `code`), or the recipient opted out and strict consent is on (`RECIPIENT_OPTED_OUT`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/scheduled":{"get":{"tags":["scheduled"],"summary":"List scheduled messages","description":"All scheduled messages of the tenant across devices, earliest first, with their current status.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Scheduled messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"chat_id":{"type":"string"},"message_type":{"type":"string"},"body":{"type":["string","null"]},"media_r2_key":{"type":["string","null"]},"caption":{"type":["string","null"]},"scheduled_at":{"type":"integer","description":"When to send, Unix time in milliseconds"},"status":{"type":"string","description":"`pending`, `sending`, `sent` or `failed`"},"message_id":{"type":["string","null"],"description":"Id of the sent message once `sent`"},"error":{"type":["string","null"],"description":"Failure reason when `failed`, e.g. `opted_out`"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","api_key_id","chat_id","message_type","body","media_r2_key","caption","scheduled_at","status","message_id","error","created_at"]}}},"required":["messages"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["scheduled"],"summary":"Schedule a message","description":"Queues one message to be sent through a device at `scheduled_at`. The server sends it once the time has passed and the device is connected; a recipient who has opted out by then is skipped and the message is marked `failed`. Limited to 20 requests per minute per key. The handler validates only that the device is yours: `chat_id`, `scheduled_at` and the content fields are stored as given, so send a full JID and a millisecond timestamp.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string","description":"Device to send from; must belong to the tenant"},"chat_id":{"type":"string","description":"Recipient JID (stored as given, not normalised)"},"scheduled_at":{"type":"integer","description":"When to send, Unix time in milliseconds"},"message_type":{"type":"string","description":"`text` (default), `image`, `document` or `audio`"},"body":{"type":"string","description":"Text content"},"media_r2_key":{"type":"string","description":"Key of an uploaded media file"}},"required":["session_id","chat_id","scheduled_at"]}}}},"responses":{"201":{"description":"Scheduled","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]}},"required":["id","status"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Device not found or not yours (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"More than 20 scheduling requests in a minute (`RATE_LIMITED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/scheduled/{id}":{"delete":{"tags":["scheduled"],"summary":"Cancel a scheduled message","description":"Deletes a scheduled message that has not been sent yet. A message that is already sending, sent or failed cannot be deleted.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Scheduled message id"},"required":true,"description":"Scheduled message id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No pending scheduled message with this id for the tenant (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/contact-groups":{"get":{"tags":["contacts"],"summary":"List contact groups","description":"Phone-book groups of the tenant, ordered by name, with the number of contacts in each.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Groups","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"name":{"type":"string"},"created_at":{"type":"integer","description":"Unix time in milliseconds"},"contact_count":{"type":"integer"}},"required":["id","api_key_id","name","created_at","contact_count"]}}},"required":["groups"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["contacts"],"summary":"Create a contact group","description":"Creates a phone-book group. The handler does not validate `name`, but it is required.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Group name"}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/contact-groups/{id}":{"delete":{"tags":["contacts"],"summary":"Delete a contact group","description":"Deletes the group. Its contacts are kept and simply lose their group.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Group id"},"required":true,"description":"Group id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Group not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/cs/agents":{"get":{"tags":["support"],"summary":"List customer-service agents","description":"Agents of the tenant, ordered by name. Secrets (access code hash, authenticator secret) are never included.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Agents","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"name":{"type":"string"},"role":{"type":"string","description":"Stored as given at creation (default `agent`)"},"is_online":{"type":"integer","description":"1 or 0"},"max_chats":{"type":["integer","null"],"description":"Concurrent chat cap"},"active_chats":{"type":["integer","null"],"description":"Chats currently assigned and unresolved"},"created_at":{"type":"integer","description":"Unix time in milliseconds"},"phone":{"type":["string","null"],"description":"Login number, digits with country code"},"has_access_code":{"type":"boolean","description":"Whether an access code is set. The code and its hash are never returned"}},"required":["id","api_key_id","name","role","is_online","max_chats","active_chats","created_at","phone","has_access_code"]}}},"required":["agents"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"post":{"tags":["support"],"summary":"Create a customer-service agent","description":"Adds an agent who can sign in to the dashboard and work the shared inbox. An agent signs in with a WhatsApp number plus one-time code (`phone`), or with an `access_code`. The phone number must not already belong to another agent or to a tenant owner login.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Display name; must contain a non-blank value"},"role":{"type":"string","description":"Stored as given (default `agent`)"},"max_chats":{"type":"integer","description":"Concurrent chat cap (default 10)"},"access_code":{"type":"string","description":"Optional login code. Stored only as a salted hash and never returned"},"phone":{"type":"string","description":"WhatsApp number used for one-time-code login; international format, or local format resolved against the business country"}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":["string","null"],"description":"Normalised login number (digits), or null when none was given"}},"required":["id","phone"]}}}},"400":{"description":"`name` is missing or blank (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"409":{"description":"The number is already used by another account (`PHONE_IN_USE`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/cs/agents/{id}":{"patch":{"tags":["support"],"summary":"Edit a customer-service agent","description":"Changes the name, online flag, chat cap and/or access code of one of the tenant's agents. Send only the fields to change. An id that does not exist (or belongs to another tenant) changes nothing but still returns `ok: true`. The phone number cannot be changed here.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Agent id"},"required":true,"description":"Agent id","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"is_online":{"type":"boolean"},"max_chats":{"type":"integer","description":"Concurrent chat cap"},"access_code":{"type":"string","description":"New login code (ignored when empty). Stored only as a salted hash"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["support"],"summary":"Delete a customer-service agent","description":"Deletes the agent. Chats assigned to them become unassigned.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Agent id"},"required":true,"description":"Agent id","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Agent not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/cs/agents/{id}/revoke-sessions":{"post":{"tags":["support"],"summary":"Sign an agent out everywhere","description":"Deletes all refresh tokens of the agent so they can no longer renew their login. An access token that was already issued stays valid until it expires. Recorded in the audit log as `agent.revoke_sessions`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Agent id"},"required":true,"description":"Agent id","name":"id","in":"path"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"revoked":{"type":"integer","description":"Number of refresh tokens deleted"}},"required":["ok","revoked"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Agent not found (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/cs/queue":{"get":{"tags":["support"],"summary":"List unassigned open chats","description":"Open chats on the tenant's devices that no agent is assigned to yet, most recent activity first. Available to agents as well as the owner.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Queue","content":{"application/json":{"schema":{"type":"object","properties":{"queue":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"chat_id":{"type":"string"},"chat_name":{"type":["string","null"]},"last_message":{"type":["string","null"]},"last_message_at":{"type":["integer","null"]},"unread_count":{"type":["integer","null"]},"is_group":{"type":["integer","null"],"description":"1 = group chat"},"agent_id":{"type":"null","description":"Always null in the queue"},"status":{"type":"string","description":"`open`"}},"required":["id","session_id","chat_id","chat_name","last_message","last_message_at","unread_count","is_group","agent_id","status"]}}},"required":["queue"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/cs/assign":{"post":{"tags":["support"],"summary":"Assign a chat to an agent","description":"Assigns a chat to one of the tenant's agents (replacing any earlier assignment) and updates the agent's active-chat count. Available to agents as well as the owner. The body is not validated beyond ownership of `agent_id` and `session_id`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Chat JID"},"session_id":{"type":"string","description":"Device the chat belongs to"},"agent_id":{"type":"string","description":"Agent to assign"}},"required":["chat_id","session_id","agent_id"]}}}},"responses":{"200":{"description":"Assigned","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The agent or the device is not the tenant's (message `Forbidden`, no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/cs/resolve":{"post":{"tags":["support"],"summary":"Mark a chat as resolved","description":"Marks a chat resolved, which removes it from the open queue, and frees one slot of the assigned agent. Available to agents as well as the owner.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_id":{"type":"string","description":"Chat JID"},"session_id":{"type":"string","description":"Device the chat belongs to"}},"required":["chat_id","session_id"]}}}},"responses":{"200":{"description":"Resolved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The device is not the tenant's (message `Forbidden`, no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/audit":{"get":{"tags":["audit"],"summary":"Read the tenant audit log","description":"Security-relevant and destructive events of the tenant, newest first, kept for 365 days. Recorded actions — account: `auth.login` (detail `otp`, `totp` or `access_code`), `tenant.self_register`, `profile.update`, `payment.completed`, `payment.rejected`, `payment.unverifiable`, `expiry.reminder_sent`, `billing.notifications`, `ai_reply.update`, `ai_kb.upload`, `ai_kb.delete` (knowledge documents and web pages for AI replies), `ai_kb.faq` (saved answers: detail `add`, `edit`, `delete` or `answer`); stores: `store.connect`, `store.update`, `store.disconnect`; access: `api_key.create`, `api_key.revoke`, `agent.create`, `agent.revoke_sessions`; privacy: `consent.config`, `consent.bulk`, `privacy.export`, `privacy.erase`, `privacy.retention`, `contact.bulk_delete`; devices: `session.create`, `session.delete`; messages: `message.send` (a message sent by a support agent; sends by the owner or through the API are in the message log instead), `message.delete`, `message.edit`, `message.unsend`; campaigns: `blast.create`, `blast.start`, `blast.cancel`, `blast.delete`; webhooks: `webhook.create`, `webhook.update`, `webhook.delete`; inbox: `chat.assign`, `chat.resolve`, and `chat.view` (a support agent opening a chat, recorded at most once an hour per agent and chat). `target` is usually the id of the object concerned and `detail` a short note; message text is never stored and phone numbers in chat ids are masked. Page through older events by passing `next_before` and `next_before_id` of the previous response as `before` and `before_id`; the cursor is a pair because a bulk action writes several events in the same millisecond.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":100,"description":"Events to return (default 100); larger values are capped at 200"},"required":false,"description":"Events to return (default 100); larger values are capped at 200","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"description":"Return only events older than this Unix time in milliseconds; use `next_before` of the previous page"},"required":false,"description":"Return only events older than this Unix time in milliseconds; use `next_before` of the previous page","name":"before","in":"query"},{"schema":{"type":["integer","null"],"description":"Tie-breaker for `before`: with an equal timestamp only events with a smaller id are returned; use `next_before_id` of the previous page"},"required":false,"description":"Tie-breaker for `before`: with an equal timestamp only events with a smaller id are returned; use `next_before_id` of the previous page","name":"before_id","in":"query"}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","description":"Event id, increasing over time"},"ts":{"type":"integer","description":"When it happened, Unix time in milliseconds"},"actor_type":{"type":"string","description":"`admin` (the owner login or an API key), `agent`, `system`, or the payment provider (`pakasir`, `creem`, `paypal`)"},"actor_id":{"type":["string","null"],"description":"Agent id for agent actions; `api-key` for some actions done with an API key"},"action":{"type":"string"},"target":{"type":["string","null"]},"detail":{"type":["string","null"]},"ip":{"type":["string","null"],"description":"Client IP address, when known"}},"required":["id","ts","actor_type","actor_id","action","target","detail","ip"]}},"next_before":{"type":["integer","null"],"description":"Cursor for the next page (`before`), or null when this was the last page"},"next_before_id":{"type":["integer","null"],"description":"Cursor for the next page (`before_id`), or null when this was the last page"}},"required":["events","next_before","next_before_id"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`) and an API key restricted to specific sessions (`KEY_SCOPE_FORBIDDEN`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/blasts":{"get":{"tags":["blast"],"summary":"List blast campaigns of all devices","description":"Every blast campaign of the tenant across devices, newest first, without pagination. Campaigns are created per device with `POST /v1/sessions/{sessionId}/blasts`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Campaigns","content":{"application/json":{"schema":{"type":"object","properties":{"campaigns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"api_key_id":{"type":"string","description":"Owning tenant"},"name":{"type":"string"},"status":{"type":"string","description":"`draft`, `running`, `paused`, `completed`, `failed` or `cancelled`"},"message_type":{"type":"string"},"body":{"type":["string","null"]},"total_recipients":{"type":["integer","null"]},"sent_count":{"type":["integer","null"]},"failed_count":{"type":["integer","null"]},"skipped_count":{"type":["integer","null"],"description":"Recipients skipped, e.g. because they opted out"},"created_at":{"type":"integer","description":"Unix time in milliseconds"},"started_at":{"type":["integer","null"],"description":"Unix time in milliseconds, or null"},"completed_at":{"type":["integer","null"],"description":"Unix time in milliseconds, or null"},"consent_attested_at":{"type":["integer","null"],"description":"When the sender attested consent, or null"}},"required":["id","session_id","api_key_id","name","status","message_type","body","total_recipients","sent_count","failed_count","skipped_count","created_at","started_at","completed_at","consent_attested_at"],"description":"The campaign row; further columns (media, caption, footer, buttons, delay settings) are included as stored"}}},"required":["campaigns"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/stats":{"get":{"tags":["analytics"],"summary":"Today's counters","description":"Counters for the dashboard home: devices, messages since midnight (server time), running blasts and active auto-reply rules. Scoped to the tenant.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Counters","content":{"application/json":{"schema":{"type":"object","properties":{"sessions_total":{"type":"integer","description":"Devices of the tenant"},"sessions_connected":{"type":"integer","description":"Devices currently connected"},"messages_sent_today":{"type":"integer","description":"Outbound messages with status `sent`, `delivered` or `read`"},"messages_received_today":{"type":"integer"},"messages_failed_today":{"type":"integer"},"blasts_running":{"type":"integer"},"auto_reply_active":{"type":"integer","description":"Active auto-reply rules"}},"required":["sessions_total","sessions_connected","messages_sent_today","messages_received_today","messages_failed_today","blasts_running","auto_reply_active"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/usage":{"get":{"tags":["analytics"],"summary":"Plan usage this month","description":"Messages sent and devices used against the plan limits for the current month (UTC), plus what the month would cost in the tenant's billing currency (minor units): plan price and overage beyond the quota.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Usage","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","description":"`YYYY-MM` (UTC)"},"plan":{"type":["string","null"],"description":"Plan name, or null when the tenant has no plan"},"sessions":{"type":"integer","description":"Devices of the tenant"},"max_sessions":{"type":["integer","null"],"description":"Device limit of the plan"},"messages_used":{"type":"integer","description":"Messages sent this month"},"messages_quota":{"type":"integer","description":"Monthly message quota; 0 = unlimited"},"messages_remaining":{"type":["integer","null"],"description":"Quota minus used (never negative); null when unlimited"},"send_per_minute":{"type":"integer","description":"Requests per minute the plan allows on `POST …messages…`, per API key"},"billing":{"type":["object","null"],"properties":{"currency":{"type":"string","description":"ISO 4217 code"},"plan_minor":{"type":"integer","description":"Plan price in minor units of `currency`"},"overage_messages":{"type":"integer","description":"Messages beyond the quota"},"overage_minor":{"type":"integer","description":"Overage charge in minor units"},"amount_due_minor":{"type":"integer","description":"`plan_minor` + `overage_minor`"}},"required":["currency","plan_minor","overage_messages","overage_minor","amount_due_minor"],"description":"null when the tenant has no plan or the plan has no price"}},"required":["period","plan","sessions","max_sessions","messages_used","messages_quota","messages_remaining","send_per_minute","billing"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/link-preview":{"get":{"tags":["messages"],"summary":"Fetch a link preview","description":"Fetches an http(s) page (max 1 MB, 5 second timeout; private and internal addresses are refused and redirects are not followed) and extracts its Open Graph title, description and image, falling back to the `<title>`. If the page cannot be fetched, or the address is refused, the call still succeeds with null fields.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"http or https URL to preview"},"required":true,"description":"http or https URL to preview","name":"url","in":"query"}],"responses":{"200":{"description":"Preview","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image":{"type":["string","null"],"description":"Image URL, as found in the page"}},"required":["url","title","description","image"]}}}},"400":{"description":"`url` is missing, not a valid URL, or not http/https (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/me":{"get":{"tags":["auth"],"summary":"Who am I","description":"Identity of the caller: the tenant, its plan and business profile, and whether the credential is the owner login (`admin`), a customer-service agent (`agent`) or an API key (`apikey`). With the platform admin secret it returns `is_super: true` and no plan.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Caller identity","content":{"application/json":{"schema":{"type":"object","properties":{"tenant_id":{"type":"string","description":"Tenant id (`__super__` for the platform admin secret)"},"name":{"type":"string","description":"Business / account name"},"phone":{"type":["string","null"],"description":"Owner login number, or the agent's own number for role `agent`"},"role":{"type":"string","enum":["admin","agent","apikey"]},"is_super":{"type":"boolean","description":"`true` only for the platform admin secret"},"agent_id":{"type":["string","null"],"description":"Role `agent` only"},"agent_name":{"type":["string","null"],"description":"Role `agent` only"},"key_id":{"type":"string","description":"Role `apikey` only: id of the key used for this request (a child key has its own id)"},"plan":{"type":["object","null"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"active_until":{"type":["integer","null"],"description":"Unix ms until which the paid plan is active, or null"},"pending":{"type":["object","null"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"starts_at":{"type":"integer"},"until":{"type":"integer"}},"required":["id","name","starts_at","until"],"description":"A paid downgrade that takes over when the current period ends"}},"required":["id","name","active_until"]},"profile":{"type":"object","properties":{"business_name":{"type":["string","null"]},"country":{"type":"string","description":"ISO 3166-1 alpha-2 code"},"timezone":{"type":"string","description":"IANA timezone"},"locale":{"type":"string","description":"`en` or `id`"},"industry":{"type":["string","null"]},"currency":{"type":"string","description":"Billing currency, `IDR` or `USD`"},"configured":{"type":"boolean"}},"required":["business_name","country","timezone","locale","industry","currency","configured"],"description":"Business profile; same data as `GET /v1/profile`. Absent for the platform admin secret"}},"required":["tenant_id","name","phone","role","plan"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"Tenant not found (`NOT_FOUND`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/analytics":{"get":{"tags":["analytics"],"summary":"Message analytics for 7, 30 or 90 days","description":"Message counts for the last N days (including today), a per-day series, an inbound heatmap (weekday x hour), the 10 busiest chats and the average time to reply in 1:1 chats. Day boundaries follow the tenant's business timezone. The result is computed at most once a minute per tenant and window (later calls within the minute get the same answer).","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","enum":["7","30","90"],"default":"7","description":"Window length in days"},"required":false,"description":"Window length in days","name":"days","in":"query"}],"responses":{"200":{"description":"Analytics","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer"},"totals":{"type":"object","properties":{"sent":{"type":"integer","description":"Outbound messages with status `sent`, `delivered` or `read`"},"received":{"type":"integer"},"failed":{"type":"integer"},"delivered":{"type":"integer","description":"Outbound messages delivered or read"},"read":{"type":"integer","description":"Outbound messages read"}},"required":["sent","received","failed","delivered","read"]},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","description":"`YYYY-MM-DD` in the business timezone"},"sent":{"type":"integer"},"received":{"type":"integer"},"failed":{"type":"integer"}},"required":["date","sent","received","failed"]},"description":"One entry per day, oldest first"},"heatmap":{"type":"array","items":{"type":"array","items":{"type":"integer"}},"description":"Inbound messages: 7 rows (weekday, 0 = Sunday) of 24 columns (hour of day, business timezone)"},"top_chats":{"type":"array","items":{"type":"object","properties":{"chat_id":{"type":"string"},"count":{"type":"integer"}},"required":["chat_id","count"]},"description":"Up to 10 chats by message count"},"avg_response_ms":{"type":["integer","null"],"description":"Average time from a customer's message to the next reply in 1:1 chats, or null when there is no data"}},"required":["days","totals","daily","heatmap","top_chats","avg_response_ms"]}}}},"400":{"description":"`days` is not 7, 30 or 90 (`VALIDATION_FAILED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/csat/results":{"get":{"tags":["analytics"],"summary":"Customer-satisfaction results","description":"Ratings (1-5) collected by the satisfaction survey over the last N days: count, average, distribution and the 50 most recent ratings.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":365,"default":30,"description":"Window in days; values outside 1-365 are clamped"},"required":false,"description":"Window in days; values outside 1-365 are clamped","name":"days","in":"query"}],"responses":{"200":{"description":"Results","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","description":"Ratings received"},"avg":{"type":"number","description":"Average rating, rounded to 2 decimals; 0 when there are none"},"distribution":{"type":"object","additionalProperties":{"type":"integer"},"description":"Ratings per score: keys `1` to `5`"},"recent":{"type":"array","items":{"type":"object","properties":{"session_id":{"type":"string"},"chat_id":{"type":"string"},"rating":{"type":"integer"},"created_at":{"type":"integer","description":"When the customer answered"}},"required":["session_id","chat_id","rating","created_at"]}}},"required":["count","avg","distribution","recent"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/inbox":{"get":{"tags":["inbox"],"summary":"List chats across all devices","description":"Chats of all the tenant's devices, most recent first. For an agent login only chats assigned to that agent or unassigned are returned. Per-device listing is `GET /v1/sessions/{sessionId}/inbox`.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":100,"description":"Values outside 1-1 000 are clamped"},"required":false,"description":"Values outside 1-1 000 are clamped","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":1000000,"default":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Chats","content":{"application/json":{"schema":{"type":"object","properties":{"inbox":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"chat_id":{"type":"string"},"chat_name":{"type":["string","null"]},"last_message":{"type":["string","null"]},"last_message_at":{"type":["integer","null"]},"unread_count":{"type":["integer","null"]},"is_group":{"type":["integer","null"],"description":"1 = group chat, 0 = 1-on-1"},"agent_id":{"type":["string","null"],"description":"Assigned agent, or null"},"status":{"type":["string","null"],"description":"`open` or `resolved`"}},"required":["id","session_id","chat_id","chat_name","last_message","last_message_at","unread_count","is_group","agent_id","status"]}}},"required":["inbox"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/messages":{"get":{"tags":["messages"],"summary":"List messages across all devices","description":"Message log of all the tenant's devices, newest first, filtered and paged with a cursor. Filters with an unrecognised value (`direction`, `status`) are ignored rather than rejected. Pass the returned `next_cursor` as `cursor` for the next page.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":100,"description":"Values outside 1-1 000 are clamped"},"required":false,"description":"Values outside 1-1 000 are clamped","name":"limit","in":"query"},{"schema":{"type":"string","description":"Only messages created at or after this time: Unix ms or a date string"},"required":false,"description":"Only messages created at or after this time: Unix ms or a date string","name":"since","in":"query"},{"schema":{"type":"string","description":"Only messages created before this time: Unix ms or a date string"},"required":false,"description":"Only messages created before this time: Unix ms or a date string","name":"until","in":"query"},{"schema":{"type":"string","description":"Only this device"},"required":false,"description":"Only this device","name":"session_id","in":"query"},{"schema":{"type":"string","enum":["inbound","outbound"]},"required":false,"name":"direction","in":"query"},{"schema":{"type":"string","enum":["queued","sent","delivered","read","failed","cancelled","deleted","blocked"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Opaque cursor `<created_at>:<id>` from a previous `next_cursor`"},"required":false,"description":"Opaque cursor `<created_at>:<id>` from a previous `next_cursor`","name":"cursor","in":"query"}],"responses":{"200":{"description":"Messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"chat_id":{"type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"message_type":{"type":"string"},"status":{"type":"string"},"body":{"type":["string","null"]},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["id","session_id","chat_id","direction","message_type","status","body","created_at"]}},"next_cursor":{"type":["string","null"],"description":"null on the last page"}},"required":["messages","next_cursor"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/media/upload-url":{"post":{"tags":["media"],"summary":"Reserve an upload key and URL","description":"Reserves a media key for the caller and returns the URL to upload the bytes to with `PUT` (same Authorization header). Use the returned `r2_key` as `media_r2_key` when sending. The body is optional; the extension of the key comes from `filename` (`bin` when absent).","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","description":"Original file name, used for its extension and for display"},"content_type":{"type":"string","description":"MIME type, e.g. `image/jpeg`"}}}}}},"responses":{"200":{"description":"Reserved","content":{"application/json":{"schema":{"type":"object","properties":{"r2_key":{"type":"string","description":"`uploads/<32 hex>.<ext>`: the value to use as `media_r2_key`"},"upload_url":{"type":"string","description":"Absolute URL for `PUT /v1/media/put/{key}`"},"method":{"type":"string","enum":["PUT"]},"expires_at":{"type":"integer","description":"One hour from now, Unix ms"},"content_type":{"type":"string","description":"Echo of the request"},"max_bytes":{"type":"integer","description":"Handler limit (25 MB); the global request limit of 10 MB applies first"}},"required":["r2_key","upload_url","method","expires_at","max_bytes"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/media/put/{key}":{"put":{"tags":["media"],"summary":"Upload bytes to a reserved key","description":"Stores the request body as the media file for a key reserved with `POST /v1/media/upload-url`. The `Content-Type` header is recorded as the file type. Only keys of the form `uploads/<32 hex>.<ext>` are accepted, and a key reserved by another tenant is refused.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)"},"required":true,"description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)","name":"key","in":"path"}],"requestBody":{"required":true,"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"size":{"type":"integer","description":"Bytes stored"}},"required":["ok","size"]}}}},"400":{"description":"The key does not have the form `uploads/<32 hex>.<ext>` (`{ error: \"Invalid key\" }`, no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"The key is reserved by another tenant (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"The file is larger than 25 MB (handler limit), or larger than the global 10 MB request limit, which applies first","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/media/upload":{"post":{"tags":["media"],"summary":"Upload a file (multipart)","description":"Uploads one file in a single multipart request and returns its media key, to use as `media_r2_key`. The key is generated by the server; the extension comes from the file name.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","description":"The file to upload, in the form field `file`","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"`uploads/<32 hex>.<ext>`"},"size":{"type":"integer","description":"Bytes stored"}},"required":["key","size"]}}}},"400":{"description":"No `file` field in the form (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"The file is larger than 25 MB (handler limit), or larger than the global 10 MB request limit, which applies first","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/media":{"get":{"tags":["media"],"summary":"List uploaded media","description":"Completed uploads of the tenant (reserved keys that have no bytes yet are left out), newest first.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":200,"description":"Values outside 1-500 are clamped"},"required":false,"description":"Values outside 1-500 are clamped","name":"limit","in":"query"}],"responses":{"200":{"description":"Media","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"filename":{"type":["string","null"]},"content_type":{"type":["string","null"]},"size":{"type":"integer","description":"Bytes"},"created_at":{"type":"integer","description":"Unix time in milliseconds"}},"required":["key","filename","content_type","size","created_at"]}}},"required":["items"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/media/{key}":{"get":{"tags":["media"],"summary":"Download a media file","description":"Returns the file bytes. Only the owning tenant can read a file. The `Content-Type` is the recorded type for images (png, jpeg, gif, webp), video (mp4, 3gpp, quicktime, webm), audio and PDF, and `application/octet-stream` for anything else. Cached privately for 5 minutes.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)"},"required":true,"description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)","name":"key","in":"path"}],"responses":{"200":{"description":"File bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"image/*":{"schema":{"type":"string","format":"binary"}},"video/*":{"schema":{"type":"string","format":"binary"}},"audio/*":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The key is not a valid media key (`{ error: \"Invalid key\" }`, no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No such file, or it belongs to another tenant (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}},"delete":{"tags":["media"],"summary":"Delete a media file","description":"Deletes the file and its record. Only the owning tenant can delete it. Message templates or recurring messages that still reference the key will no longer find the file.","security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)"},"required":true,"description":"Media key as returned by the upload, `uploads/<32 hex>.<ext>`. The slash is part of the path (not percent-encoded)","name":"key","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"The key is not a valid media key (`{ error: \"Invalid key\" }`, no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"Forbidden for a customer-service agent login (`Agent access is not allowed for this operation`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No such file, or it belongs to another tenant (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"The file could not be removed from storage (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/auth/login":{"post":{"tags":["auth"],"security":[],"summary":"Sign in with an access code","description":"Dashboard sign-in with the tenant owner's or a customer-service agent's access code. Returns a JWT valid for 2 hours, to be sent as `Authorization: Bearer <token>`. No refresh token is issued here (sign in with a one-time code via `/auth/verify-otp` to get one). Limited to 10 attempts per 10 minutes per IP address.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":128,"description":"Access code"}},"required":["code"]}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT for the `Authorization: Bearer` header"},"role":{"type":"string","enum":["admin","agent"]},"agent_id":{"type":"string","description":"Role `agent` only"},"agent_name":{"type":"string","description":"Role `agent` only"}},"required":["token","role"]}}}},"400":{"description":"`code` is missing or longer than 128 characters (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Invalid access code (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many attempts from this IP address (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/auth/logout":{"post":{"tags":["auth"],"security":[{},{"bearerAuth":[]}],"summary":"Sign out","description":"Ends a dashboard login (owner or agent). Send the refresh token in the body, the access token as `Authorization: Bearer`, or both; at least one is required. The refresh token is deleted and the login session is revoked, so the access token stops working immediately. Idempotent. The bearer token is optional here and only a JWT counts (an API key is ignored).","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"refresh_token":{"type":"string","maxLength":256,"description":"Refresh token issued at sign-in"}}}}}},"responses":{"200":{"description":"Signed out","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Neither a refresh token nor a valid access token was supplied (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/auth/verify-totp":{"post":{"tags":["auth"],"security":[],"summary":"Sign in with an authenticator code","description":"Fallback sign-in for a tenant owner when the WhatsApp one-time code cannot be delivered. Needs two-factor authentication to be set up on the account. Accepts the 6-digit code of the previous, current or next 30-second window. Failed codes lock the account for increasing periods (3 failures: 5 minutes, 5: 1 hour, 10: 24 hours) and each IP address is limited to 8 requests per 10 minutes (counted together with `/auth/request-otp` and `/auth/register`). Returns a 2-hour JWT and a refresh token. Customer-service agents cannot use this endpoint.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"Owner login number, international format `+<country code><number>`"},"code":{"type":"string","description":"6-digit authenticator code"}},"required":["phone","code"]}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"JWT for the `Authorization: Bearer` header"},"role":{"type":"string","enum":["admin"]},"refresh_token":{"type":"string","description":"Rotating refresh token, valid 30 days. Exchange it at `/auth/refresh`"}},"required":["token","role","refresh_token"]}}}},"400":{"description":"`phone` or `code` missing (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"2FA is not enabled for this account (also returned for an unknown number), or the code is wrong (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"Too many requests from this IP address, or the account is locked after repeated wrong codes (no `code`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/tenants/{id}":{"delete":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Delete a tenant permanently","description":"Super-admin only. Irreversible: disconnects and removes every WhatsApp session of the tenant (stored WhatsApp credentials are wiped), deletes its message log, inbox, blasts and auto-replies, every other row that belongs to the tenant, its per-tenant settings, dashboard logins, child API keys and finally the tenant key. The body must repeat the tenant name as `confirm`. The primary tenant `key-main` cannot be deleted. Audited as `tenant.delete`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string","description":"Must equal the tenant's current `name` exactly"}},"required":["confirm"]}}}},"responses":{"200":{"description":"Tenant and its data deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"string","description":"Name of the deleted tenant"},"sessions":{"type":"integer","description":"Number of WhatsApp sessions that were removed"}},"required":["ok","deleted","sessions"]}}}},"400":{"description":"`Cannot delete primary tenant` (id `key-main`) or `Confirmation name mismatch` (`confirm` missing or different from the tenant name). No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No such tenant (`{ \"error\": \"Not found\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/tenants/{id}/activate":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Reactivate a suspended tenant","description":"Super-admin only. Clears the suspension of the tenant key and asks each of its WhatsApp sessions to reconnect (stored credentials are kept). An unknown id is not rejected: the call still answers `ok` with `reconnected: 0`. Audited as `tenant.activate`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"}],"responses":{"200":{"description":"Tenant active again","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"suspended":{"type":"boolean","description":"Always `false`"},"reconnected":{"type":"integer","description":"Number of the tenant's sessions a reconnect was requested for"}},"required":["ok","suspended","reconnected"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/tenants/{id}/suspend":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Suspend a tenant","description":"Super-admin only. Marks the tenant key as revoked, so its dashboard logins, API keys and child keys stop authenticating (401), and disconnects its WhatsApp sessions without wiping their credentials. Undo with `POST /v1/admin/tenants/{id}/activate`. An unknown id is not rejected. Audited as `tenant.suspend`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"}],"responses":{"200":{"description":"Tenant suspended","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"suspended":{"type":"boolean","description":"Always `true`"},"disconnected":{"type":"integer","description":"Number of the tenant's sessions that were told to disconnect"}},"required":["ok","suspended","disconnected"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/tenants/{id}/plan":{"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Change a tenant's plan","description":"Super-admin only. Sets the tenant's plan immediately, without a payment. The handler checks neither that the tenant nor that the plan exists, and it leaves the paid-through date (`plan_active_until`) alone, so a tenant whose paid-through date already passed is moved back to Free by the periodic plan-expiry job. Audited as `tenant.plan_change`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"integer","description":"Plan id (see `GET /v1/admin/plans`). Required; `0` counts as missing"}},"required":["plan_id"]}}}},"responses":{"200":{"description":"Plan set","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"plan_id":{"type":"integer"}},"required":["ok","plan_id"]}}}},"400":{"description":"`plan_id required`. No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/tenants/{id}/billing":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Current-month billing of a tenant","description":"Super-admin only. What the tenant owes for the current calendar month (UTC): the plan's monthly price plus overage, which is the messages sent beyond the plan quota times the plan's per-message overage rate. Amounts are in IDR, the plan's own `price_monthly`; prices in other currencies are not included.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"}],"responses":{"200":{"description":"Billing snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"type":"string","description":"Tenant name"},"period":{"type":"string","description":"`YYYY-MM`"},"plan":{"type":["string","null"],"description":"Plan name; `null` when the tenant has no plan"},"quota":{"type":["integer","null"],"description":"Plan message quota for the month; overage only applies when it is above 0"},"used":{"type":"integer","description":"Messages sent this month"},"overage":{"type":"integer","description":"Overage charge so far (IDR); 0 when within the quota or the plan has no overage rate"},"amount_due":{"type":"integer","description":"Plan monthly price plus `overage` (IDR)"}},"required":["tenant","period","plan","quota","used","overage","amount_due"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"No such tenant (`{ \"error\": \"Not found\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/tenants/{id}/health":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Health of one tenant","description":"Super-admin only, read-only. Every device of the tenant with a `down` flag (set up but not connected), plus message volume per day and in total over the last 7, 30 or 90 days, with days cut at the tenant's own midnight. Only top-level tenants are accepted; a derived API key id is a 404.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)"},"required":true,"description":"Tenant id: the `id` of a row of `GET /v1/admin/tenants` (a top-level tenant API key)","name":"id","in":"path"},{"schema":{"type":"string","enum":["7","30","90"],"default":"30","description":"Window length in days"},"required":false,"description":"Window length in days","name":"days","in":"query"}],"responses":{"200":{"description":"Tenant health","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTenantHealth"}}}},"400":{"description":"`VALIDATION_FAILED`: `days` is not 7, 30 or 90","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"404":{"description":"`NOT_FOUND`: no such tenant","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/audit":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Platform-wide audit log","description":"Super-admin only. Newest-first audit events of the whole platform: operator actions, tenant lifecycle, settings changes and payment settlements. A tenant's own events are at `GET /v1/audit`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100,"description":"Number of events. Values above 500 are capped at 500; a value that is not a number counts as 100"},"required":false,"description":"Number of events. Values above 500 are capped at 500; a value that is not a number counts as 100","name":"limit","in":"query"},{"schema":{"type":"string","description":"Only events with exactly this `action`, e.g. `tenant.suspend`"},"required":false,"description":"Only events with exactly this `action`, e.g. `tenant.suspend`","name":"action","in":"query"},{"schema":{"type":"string","description":"Only events whose `api_key_id` or `target` equals this tenant id"},"required":false,"description":"Only events whose `api_key_id` or `target` equals this tenant id","name":"tenant","in":"query"}],"responses":{"200":{"description":"Audit events, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"ts":{"type":"integer","description":"When it happened (Unix epoch, milliseconds)"},"actor_type":{"type":"string","description":"`super-admin`, `admin`, `agent`, `system`, or a payment provider (`pakasir`, `creem`, `paypal`)"},"actor_id":{"type":["string","null"]},"api_key_id":{"type":["string","null"],"description":"Tenant the event belongs to, when it has one"},"action":{"type":"string","description":"Dotted action name, e.g. `tenant.provision`, `tenant.suspend`, `settings.pakasir`, `payment.completed`"},"target":{"type":["string","null"],"description":"Object acted on, e.g. a tenant id or an invoice order id"},"detail":{"type":["string","null"],"description":"Short free text; phone numbers and similar personal data are redacted before storage"},"ip":{"type":["string","null"]}},"required":["id","ts","actor_type","actor_id","api_key_id","action","target","detail","ip"]}}},"required":["events"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}}}},"/v1/admin/backups":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"List database backups","description":"Super-admin only. The backup files kept on the server (`data/backups`), newest first. The gateway also writes one automatically every day at 03:00 server time and keeps the 7 newest.","responses":{"200":{"description":"Backups","content":{"application/json":{"schema":{"type":"object","properties":{"backups":{"type":"array","items":{"type":"object","properties":{"filename":{"type":"string","description":"`gateway-<timestamp>.db`"},"size_mb":{"type":"number","description":"Size in megabytes, 2 decimals"},"created":{"type":"string","description":"UTC time taken from the file name, `YYYY-MM-DDTHH:MM:SS` without a timezone suffix"}},"required":["filename","size_mb","created"]}}},"required":["backups"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/backup":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Back up the database now","description":"Super-admin only. Writes a consistent snapshot of the SQLite database to `data/backups/gateway-<timestamp>.db` and deletes all but the 7 newest backups. The file stays on the server; nothing is uploaded elsewhere by this call.","responses":{"200":{"description":"Backup written","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"filename":{"type":"string"},"size_mb":{"type":"number","description":"Size in megabytes, 2 decimals"},"retained":{"type":"integer","description":"Backups kept after pruning (at most 7)"}},"required":["ok","filename","size_mb","retained"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/cleanup":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Run message retention now","description":"Super-admin only. Permanently deletes messages older than the retention window and inbox conversations that are older than it with nothing unread. Tenants that chose their own retention window are purged by that window; everyone else by the platform default. The same purge runs automatically every 24 hours.","responses":{"200":{"description":"Purge result","content":{"application/json":{"schema":{"type":"object","properties":{"deleted_messages":{"type":"integer"},"deleted_inbox":{"type":"integer","description":"Inbox conversations removed"},"retention_days":{"type":"integer","description":"Platform default window in days"},"tenants_with_custom_retention":{"type":"integer","description":"Tenants purged by their own window instead"}},"required":["deleted_messages","deleted_inbox","retention_days","tenants_with_custom_retention"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/cleanup-media":{"post":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Delete unreferenced media files","description":"Super-admin only. Permanently deletes every file in the gateway's local media folder that no logged message points to (`media_r2_key`). If the folder does not exist the call succeeds and reports zeros with a `note`.","responses":{"200":{"description":"Cleanup result","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"Files deleted"},"freed_mb":{"type":"number","description":"Space freed in megabytes, 2 decimals"},"total_files":{"type":"integer","description":"Files that were in the folder before the cleanup"},"note":{"type":"string","description":"`media dir not found` when there was no folder"}},"required":["deleted","freed_mb","total_files"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"500":{"description":"The folder could not be processed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/db-stats":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Database size and message counts","description":"Super-admin only. Row counts of the message log and the inbox, the age of the oldest message, the database size on disk and the platform default retention window.","responses":{"200":{"description":"Database statistics","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"integer","description":"Rows in the message log"},"inbox_chats":{"type":"integer","description":"Rows in the inbox (conversations)"},"oldest_message":{"type":["integer","null"],"description":"Creation time of the oldest message (Unix epoch, milliseconds); `null` when there are none"},"db_size_mb":{"type":"number","description":"Database file size in megabytes, 2 decimals"},"retention_days":{"type":"integer","description":"Platform default retention window"}},"required":["messages","inbox_chats","oldest_message","db_size_mb","retention_days"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/secrets-status":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Secret rotation schedule","description":"Super-admin only. Rotation bookkeeping for the gateway's own secrets (`JWT_SECRET`, `VPS_SECRET`). Secret values are never returned: `current_value` holds the placeholder `(set in .env)`. The gateway only tracks the 30-day schedule and logs a warning when a rotation is overdue; it does not rotate anything itself.","responses":{"200":{"description":"Rotation rows","content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"type":"object","properties":{"key_name":{"type":"string","description":"Secret name, e.g. `JWT_SECRET`"},"current_value":{"type":"string","description":"Placeholder text written by the gateway, not the secret"},"rotated_at":{"type":"integer","description":"When the secret was last recorded as rotated (Unix epoch, milliseconds)"},"next_rotation_at":{"type":"integer","description":"When the next rotation is due (Unix epoch, milliseconds)"}},"required":["key_name","current_value","rotated_at","next_rotation_at"]}}},"required":["secrets"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/otp-sender":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Get the OTP sender session","description":"Super-admin only. The WhatsApp session that sends login one-time passwords and plan-expiry reminders, plus every session on the platform (all tenants, newest first) to choose from.","responses":{"200":{"description":"Current setting","content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":["string","null"],"description":"Chosen session id; `null` when none is set"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","description":"Stored session status"},"connected":{"type":"boolean","description":"`true` when this gateway process holds a live connection for it"}},"required":["id","status","connected"]}}},"required":["session_id","sessions"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Set the OTP sender session","description":"Super-admin only. Chooses the session that sends login OTPs and expiry reminders. The id is not checked against existing sessions: pick a connected one from `GET /v1/admin/settings/otp-sender`, otherwise OTP delivery fails. Audited as `settings.otp_sender`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string","description":"Session id"}},"required":["session_id"]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"session_id":{"type":"string"}},"required":["ok","session_id"]}}}},"400":{"description":"`session_id required`. No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/settings/pakasir":{"get":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Get Pakasir settings","description":"Super-admin only. The Pakasir project slug and whether an API key and a webhook secret are stored. The secrets themselves are never returned.","responses":{"200":{"description":"Pakasir settings","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","description":"Pakasir project slug; empty when not set"},"has_api_key":{"type":"boolean"},"has_webhook_secret":{"type":"boolean","description":"Whether the value of the `X-Secret` header Pakasir sends on webhooks is stored (the `PAKASIR_WEBHOOK_SECRET` environment variable is used when it is not)"}},"required":["slug","has_api_key","has_webhook_secret"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]},"put":{"tags":["admin"],"security":[{"adminSecret":[]}],"summary":"Set Pakasir settings","description":"Super-admin only. Send only the fields to change. `slug` is written whenever it is present (an empty string clears it); `api_key` and `webhook_secret` are written only when non-empty, so a blank value keeps the stored secret. Nothing is validated. Audited as `settings.pakasir`.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","description":"Pakasir project slug"},"api_key":{"type":"string","description":"Pakasir API key (write-only)"},"webhook_secret":{"type":"string","description":"Value of the `X-Secret` header Pakasir sends on webhook deliveries (write-only)"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"No valid credentials: no `X-Admin-Secret` match and no valid Bearer token. Repeated wrong secrets from one IP are answered `429` for a while","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is signed in as a tenant admin, agent or API key, not as the super-admin (`{ \"error\": \"Super-admin required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/totp/status":{"get":{"tags":["admin"],"security":[{"bearerAuth":[]}],"summary":"Is 2FA enabled?","description":"Tenant admin only (dashboard Bearer JWT with role `admin`; API keys and agent tokens get 403). Whether the signed-in tenant has authenticator-app 2FA switched on. When it is, the admin can also sign in with the app's code via `POST /auth/verify-totp`, for example when the WhatsApp OTP sender is down. TOTP is not a step-up check on any other admin route.","responses":{"200":{"description":"2FA state","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"401":{"description":"Missing, invalid or expired Bearer token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is not a tenant admin: an API key, an agent token or a session-scoped key (`{ \"error\": \"Admin access required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/totp/setup":{"post":{"tags":["admin"],"security":[{"bearerAuth":[]}],"summary":"Start 2FA enrolment","description":"Tenant admin only. Generates a new authenticator secret and stores it as pending; 2FA stays off until `POST /v1/admin/totp/verify` confirms a code. Calling it again replaces the pending secret. The secret is returned only here; treat the response as confidential.","responses":{"200":{"description":"Pending secret","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string","description":"Shared secret, base32 (RFC 4648), for manual entry in an authenticator app"},"otpauth":{"type":"string","description":"`otpauth://totp/…` URI to render as a QR code"},"message":{"type":"string"}},"required":["secret","otpauth","message"]}}}},"401":{"description":"Missing, invalid or expired Bearer token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is not a tenant admin: an API key, an agent token or a session-scoped key (`{ \"error\": \"Admin access required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/totp/verify":{"post":{"tags":["admin"],"security":[{"bearerAuth":[]}],"summary":"Confirm 2FA enrolment","description":"Tenant admin only. Checks a code against the pending secret from `POST /v1/admin/totp/setup` and, if it matches, switches 2FA on. After 5 wrong codes the tenant is locked out of verify and disable for 15 minutes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current 6-digit code from the authenticator app (the code of the previous and the next 30-second step is accepted too)"}},"required":["code"]}}}},"responses":{"200":{"description":"2FA activated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["ok","message"]}}}},"400":{"description":"`code required` (missing or not a string) or `No TOTP setup pending`. No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"`Invalid code`, or missing/invalid Bearer token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is not a tenant admin: an API key, an agent token or a session-scoped key (`{ \"error\": \"Admin access required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"`Too many attempts, try again later`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/totp/disable":{"post":{"tags":["admin"],"security":[{"bearerAuth":[]}],"summary":"Turn 2FA off","description":"Tenant admin only. Switches authenticator 2FA off after checking a current code. Uses the same 5-failures / 15-minute lockout as `POST /v1/admin/totp/verify`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current 6-digit code from the authenticator app (the code of the previous and the next 30-second step is accepted too)"}},"required":["code"]}}}},"responses":{"200":{"description":"2FA disabled","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["ok","message"]}}}},"400":{"description":"`code required` (missing or not a string) or `2FA not enabled`. No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"`Invalid code`, or missing/invalid Bearer token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is not a tenant admin: an API key, an agent token or a session-scoped key (`{ \"error\": \"Admin access required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"429":{"description":"`Too many attempts, try again later`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/v1/admin/csat-config":{"put":{"tags":["admin"],"security":[{"bearerAuth":[]}],"summary":"Save the CSAT survey config","description":"Tenant admin only. Stores the JSON body as the signed-in tenant's `csat_config` setting, replacing the previous value; the body is not validated beyond being JSON. The same setting is readable and writable through `/v1/settings/csat_config`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Send the satisfaction survey when a chat is closed"},"question":{"type":"string","description":"Survey text sent to the customer (1-5 reply)"},"thank_you":{"type":"string","description":"Reply sent after the customer rates"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"`Invalid JSON body` (unparseable, or the literal `null`). No `code` is returned","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"Missing, invalid or expired Bearer token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"403":{"description":"The caller is not a tenant admin: an API key, an agent token or a session-scoped key (`{ \"error\": \"Admin access required\" }`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}},"/pakasir/webhook":{"post":{"tags":["billing"],"security":[],"summary":"Pakasir payment webhook","description":"Called by Pakasir, not by integrators. When a webhook secret is configured (setting or `PAKASIR_WEBHOOK_SECRET`) the `X-Secret` header must match it; with none configured the header is not checked and the status check below is the only authority. Whatever the payload says, the payment is re-read from Pakasir's transaction-status API and the invoice is activated only if it is paid for exactly that order, amount and currency. Unknown and already-paid orders are acknowledged with `{ \"ok\": true }`.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Webhook secret configured in the Pakasir project. Required once a secret is configured here; compared in constant time"},"required":false,"description":"Webhook secret configured in the Pakasir project. Required once a secret is configured here; compared in constant time","name":"x-secret","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"order_id":{"type":"string","description":"Our invoice order id (as sent to Pakasir at checkout)"},"txn_id":{"type":"string","description":"Pakasir transaction id; used only when the invoice has none stored"}},"required":["order_id"]}}}},"responses":{"200":{"description":"Acknowledged: the provider should stop retrying this delivery","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentWebhookAck"}}}},"400":{"description":"`bad`: `order_id` is missing or not a string","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"`unauthorized`: a secret is configured and `X-Secret` is missing or different","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"Pakasir is not configured here, or its status API could not be reached. Answering non-2xx makes Pakasir deliver again later","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"`unconfigured`, `verification unavailable` or `payment could not be verified`"},"status":{"type":"string","description":"`unverified` when the payment could not be verified"}},"required":["error"]}}}}}}},"/creem/webhook":{"post":{"tags":["billing"],"security":[],"summary":"Creem payment webhook","description":"Called by Creem, not by integrators. Authenticated by the `creem-signature` header, the hex HMAC-SHA256 of the exact raw body under the Creem webhook secret (compared in constant time); bodies over 256 KiB are refused. `checkout.completed` events are matched to an invoice by `object.request_id` (our order id) or `object.id`, then the checkout is re-read from Creem before the plan is activated. `refund.created` and `dispute.created` are only audited for manual review; every other event type is acknowledged and ignored.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string","description":"Hex HMAC-SHA256 of the raw request body, keyed with the Creem webhook secret"},"required":true,"description":"Hex HMAC-SHA256 of the raw request body, keyed with the Creem webhook secret","name":"creem-signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Event id"},"eventType":{"type":"string","description":"e.g. `checkout.completed`, `refund.created`, `dispute.created`"},"object":{"type":"object","properties":{"id":{"type":"string","description":"Creem checkout id"},"request_id":{"type":"string","description":"Our invoice order id"}}}},"required":["eventType"]}}}},"responses":{"200":{"description":"Acknowledged: the provider should stop retrying this delivery","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentWebhookAck"}}}},"400":{"description":"`bad`: the body is not valid JSON (the signature was valid)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"401":{"description":"`unauthorized`: `creem-signature` is missing or does not match","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"`payload too large` (over 256 KiB)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"Retry later: the Creem webhook secret is not configured (`unconfigured`), the payment could not be verified at Creem (`payment could not be verified`) or Creem could not be reached (`verification unavailable`). Creem delivers again","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"`unconfigured`, `verification unavailable` or `payment could not be verified`"},"status":{"type":"string","description":"`unverified` when the payment could not be verified"}},"required":["error"]}}}}}}},"/paypal/webhook":{"post":{"tags":["billing"],"security":[],"summary":"PayPal payment webhook","description":"Called by PayPal, not by integrators. Authenticated through PayPal's own signature verification: the five `paypal-*` transmission headers and the raw body are posted to PayPal together with our configured webhook id, and only a `SUCCESS` verdict is accepted (bodies over 256 KiB are refused). `CHECKOUT.ORDER.APPROVED` and `PAYMENT.CAPTURE.COMPLETED` are matched to an invoice by the order's `custom_id` / `invoice_id`, then the order is re-read from PayPal before the plan is activated. Refunds, reversals and disputes are only audited for manual review; other event types are acknowledged and ignored.","parameters":[{"$ref":"#/components/parameters/XLang"},{"schema":{"type":"string"},"required":true,"name":"paypal-auth-algo","in":"header"},{"schema":{"type":"string"},"required":true,"name":"paypal-cert-url","in":"header"},{"schema":{"type":"string"},"required":true,"name":"paypal-transmission-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"paypal-transmission-sig","in":"header"},{"schema":{"type":"string"},"required":true,"name":"paypal-transmission-time","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_type":{"type":"string","description":"e.g. `CHECKOUT.ORDER.APPROVED`, `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.REFUNDED`"},"resource":{"type":"object","properties":{},"description":"The PayPal order or capture; only its ids and our `custom_id` / `invoice_id` are read"}},"required":["event_type"]}}}},"responses":{"200":{"description":"Acknowledged: the provider should stop retrying this delivery","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentWebhookAck"}}}},"401":{"description":"`unauthorized`: a signature header is missing, the body is not JSON, or PayPal did not confirm the signature","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"413":{"description":"`payload too large` (over 256 KiB)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message. English by default; localized (currently English or Indonesian) when the request sets `X-Lang`, `?lang=` or `Accept-Language`. Never parse this text: branch on `code`."},"code":{"type":"string","description":"Stable machine-readable error code, identical in every language. Common values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, RATE_LIMITED, CONFLICT, NOT_CONNECTED, LIMIT_REACHED, INVALID_NUMBER, INVALID_JID, INVALID_TARGETS, RECIPIENT_OPTED_OUT, CONSENT_ATTESTATION_REQUIRED, CONFIRMATION_REQUIRED, CURRENCY_NOT_SUPPORTED, PHONE_IN_USE, WEBHOOK_URL_BLOCKED, UNSAFE_REGEX, KEY_MANAGEMENT_REQUIRES_LOGIN, CANNOT_REVOKE_PRIMARY. A few legacy errors carry no code; use the HTTP status then."}},"required":["error"]}}}},"503":{"description":"Retry later: no PayPal webhook id is configured (`unconfigured`), PayPal could not be reached (`verification unavailable`) or the payment could not be verified (`payment could not be verified`). PayPal delivers again","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"`unconfigured`, `verification unavailable` or `payment could not be verified`"},"status":{"type":"string","description":"`unverified` when the payment could not be verified"}},"required":["error"]}}}}}}},"/health":{"get":{"tags":["system"],"security":[],"summary":"Liveness probe","description":"Public, unauthenticated. Returns a fixed status and the API version, and nothing about tenants, sessions or load.","responses":{"200":{"description":"The gateway is up","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string","description":"API version, e.g. `1.0.0`"}},"required":["status","version"]}}}}},"parameters":[{"$ref":"#/components/parameters/XLang"}]}}},"webhooks":{}}