API reference · 2026-10-01
Message QuiC users who opted in to your app. No message is ever delivered without the user's opt-in. Business chats are NOT end-to-end encrypted. Free during the preview; rate limits and quotas apply.
Base URL https://api.quic.chat/platform/v1. Authenticate with Authorization: Bearer qk_… (keys & scopes ). Errors use one envelope . Webhook payloads are in the events reference .
Download openapi.json (OpenAPI 3.1.0) to import into Postman, Insomnia or a code generator.
Messages# post /messages — Send a message Queues a text or media message to a user who opted in. Sandbox keys reach accepted testers only; internal_live apps reach their own organization (and may address by work email with a live key); public_live apps reach any organization that allows external businesses. Outside the 24h window after the user's last message an app may send at most 3 messages per user per 24h.
Scope: messages:send
Parameters
Idempotency-Key (header, string, required) — Required. Any unique string (<=255 printable ASCII). Kept 24h: same key + same body replays the first response; same key + different body is 422.Request body
POST /messages request body Field Type Notes torequired object | object to (option 1).appUserIdrequired string Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz · pattern ^u_[0-9A-Za-z]{22}$ to (option 2).emailrequired string (email) internal_live apps with a live key only (D4) · max length 254 typerequired "text" | "image" | "video" | "audio" | "file" text string Required for text; caption for media · max length 4096 mediaId string From POST /media; required for media types · min length 1 · max length 64 clientReference string Echoed in message.status events · min length 1 · max length 128
Response 202 — Accepted
POST /messages response 202 Field Type Notes idrequired string statusrequired "accepted" appUserIdrequired string Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz · pattern ^u_[0-9A-Za-z]{22}$ typerequired "text" | "image" | "video" | "audio" | "file" createdAtrequired string (date-time) clientReferencerequired string | null
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
get /messages/{id} — Get a message Scope: messages:read
Parameters
id (path, string, required) — Message idResponse 200 — The message
GET /messages/{id} response 200 Field Type Notes idrequired string directionrequired "outbound" | "inbound" appUserIdrequired string | null Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz typerequired string textrequired string | null mediarequired object[] media[].idrequired string media[].mimeTyperequired string | null media[].fileNamerequired string media[].sizerequired integer statusrequired "sent" | "delivered" | "read" | "received" clientReferencerequired string | null createdAtrequired string (date-time) deliveredAtrequired string (date-time) | null readAtrequired string (date-time) | null
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
post /media — Create an upload Returns a presigned PUT URL. Upload the bytes with the same Content-Type within 15 minutes, then reference media.id in POST /messages. Images, video and audio up to 16 MB; documents up to 100 MB. 100 uploads per hour.
Scope: media:write
Request body
POST /media request body Field Type Notes fileNamerequired string min length 1 · max length 255 mimeTyperequired "image/jpeg" | "image/png" | "image/webp" | "image/gif" | "video/mp4" | "video/3gpp" | "video/quicktime" | "audio/mpeg" | "audio/mp4" | "audio/aac" | "audio/ogg" | "audio/amr" | "application/pdf" | "text/plain" | "text/csv" | "application/msword" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.ms-powerpoint" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" sizerequired integer Bytes. Images/video/audio <= 16 MB, documents <= 100 MB · ≤ 104857600
Response 201 — Upload created
POST /media response 201 Field Type Notes mediarequired object media.idrequired string media.sourcerequired "app" | "user" media.kindrequired "image" | "video" | "audio" | "document" | "other" media.mimeTyperequired string media.fileNamerequired string media.sizerequired integer media.statusrequired "pending_upload" | "ready" media.urlrequired string | null Presigned download URL, valid 1 hour media.urlExpiresAtrequired string (date-time) | null media.createdAtrequired string (date-time) uploadrequired object upload.urlrequired string PUT the bytes here within 15 minutes upload.methodrequired "PUT" upload.headersrequired object upload.expiresAtrequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
Your uploads, or media a user sent you (message.received media ids). Returns a 1-hour download URL.
Scope: messages:read
Parameters
id (path, string, required) — Media idResponse 200 — The media
GET /media/{id} response 200 Field Type Notes idrequired string sourcerequired "app" | "user" kindrequired "image" | "video" | "audio" | "document" | "other" mimeTyperequired string fileNamerequired string sizerequired integer statusrequired "pending_upload" | "ready" urlrequired string | null Presigned download URL, valid 1 hour urlExpiresAtrequired string (date-time) | null createdAtrequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
Users & consent# get /users/{appUserId} — Get a user's consent + window Scope: consents:read
Parameters
appUserId (path, string, required) — Per-app user idResponse 200 — The user
GET /users/{appUserId} response 200 Field Type Notes appUserIdrequired string Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz · pattern ^u_[0-9A-Za-z]{22}$ consentrequired object consent.appUserIdrequired string Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz · pattern ^u_[0-9A-Za-z]{22}$ consent.statusrequired "opted_in" | "opted_out" | "blocked" consent.sourcerequired "user_initiated" | "profile_allow" | "opt_in_link" | "sandbox_tester" consent.refrequired string | null consent.optedInAtrequired string (date-time) | null consent.optedOutAtrequired string (date-time) | null consent.updatedAtrequired string (date-time) windowrequired object window.openrequired boolean true within 24h of the user's last message window.expiresAtrequired string (date-time) | null
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
get /consents — List consents Scope: consents:read
Parameters
status (query, "opted_in" | "opted_out" | "blocked") limit (query, integer) cursor (query, string) Response 200 — A page of consents
GET /consents response 200 Field Type Notes datarequired object[] data[].appUserIdrequired string Per-app user id, e.g. u_4f9XkQ2bT7mN1pR8sV0wYz · pattern ^u_[0-9A-Za-z]{22}$ data[].statusrequired "opted_in" | "opted_out" | "blocked" data[].sourcerequired "user_initiated" | "profile_allow" | "opt_in_link" | "sandbox_tester" data[].refrequired string | null data[].optedInAtrequired string (date-time) | null data[].optedOutAtrequired string (date-time) | null data[].updatedAtrequired string (date-time) nextCursorrequired string | null
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
post /optin-links — Create an opt-in link A signed link to your business profile. Opening it never opts the user in; they must tap Allow in QuiC. The ref comes back in consent.updated.
Scope: optin_links:create
Request body
POST /optin-links request body Field Type Notes ref string Your own reference, echoed in consent.updated · pattern ^[A-Za-z0-9_.:-]{1,64}$ expiresInDays integer ≥ 1 · ≤ 90
Response 201 — Link created
POST /optin-links response 201 Field Type Notes urlrequired string tokenrequired string refrequired string | null expiresAtrequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
Business profile# get /business-profile — Get your business profile Response 200 — Profile
GET /business-profile response 200 Field Type Notes appIdrequired string handlerequired string displayNamerequired string avatarUrlrequired string | null aboutrequired string | null websiteUrlrequired string | null domainrequired string | null categoryrequired string | null verificationStatusrequired "unverified" | "pending" | "verified" | "revoked" verifiedAtrequired string (date-time) | null updatedAtrequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
patch /business-profile — Update your business profile Scope: profile:write
Request body
PATCH /business-profile request body Field Type Notes displayName string min length 2 · max length 60 about string | null websiteUrl string (uri) | null category string | null avatarMediaId string | null A ready image from POST /media, <= 5 MB
Response 200 — Profile
PATCH /business-profile response 200 Field Type Notes appIdrequired string handlerequired string displayNamerequired string avatarUrlrequired string | null aboutrequired string | null websiteUrlrequired string | null domainrequired string | null categoryrequired string | null verificationStatusrequired "unverified" | "pending" | "verified" | "revoked" verifiedAtrequired string (date-time) | null updatedAtrequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
Diagnostics# get /ping — Check a key Response 200 — Key is valid
GET /ping response 200 Field Type Notes okrequired true appIdrequired string appStatusrequired string environmentrequired "sandbox" | "live" scopesrequired ("messages:send" | "messages:read" | "media:write" | "consents:read" | "optin_links:create" | "profile:write" | "webhooks:manage" | "templates:manage")[] apiVersionrequired string requestIdrequired string timerequired string (date-time)
Errors: 400 invalid_request / invalid_json / idempotency_key_required, 401 invalid_api_key , 403 insufficient_scope / app_suspended / key_environment_mismatch / consent_required / recipient_not_tester / recipient_outside_org / recipient_org_blocks_businesses , 404 not_found , 409 idempotency_in_progress / media_not_uploaded , 422 idempotency_key_reused / media_invalid , 429 rate_limited / daily_quota_exceeded / business_initiated_cap / media_rate_limited . See error codes .
← Encryption & privacy Changelog →