Messages
Sending messages
POST /messages queues a text or media message to one user who opted in, and answers 202 Accepted.
curl https://api.quic.chat/platform/v1/messages \
-H "Authorization: Bearer $QUIC_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"to": { "appUserId": "u_4f9XkQ2bT7mN1pR8sV0wYz" },
"type": "text",
"text": "Hello from my first QuiC app π",
"clientReference": "order-1042"
}'HTTP/1.1 202 Accepted
QuiC-Request-Id: req_...
QuiC-Version: 2026-10-01
{
"id": "cmg4r2x0p0001ab12cd34ef56",
"status": "accepted",
"appUserId": "u_4f9XkQ2bT7mN1pR8sV0wYz",
"type": "text",
"createdAt": "2026-10-01T09:30:00.000Z",
"clientReference": "order-1042"
}Request body#
| Field | Type | Notes |
|---|---|---|
to | object | Either { "appUserId": "u_β¦" } or, for internal live apps with a live key, { "email": "β¦" } for someone in your organization. |
type | string | text, image, video, audio or file. |
text | string β€ 4096 | Required for text; an optional caption for media. |
mediaId | string | From POST /media; required for media types. |
clientReference | string β€ 128 | Your own id, echoed in message.status events. |
Idempotency#
Idempotency-Key is required: any unique string up to 255 characters, such as a UUID. QuiC keeps it for 24 hours. Retrying with the same key and the same body returns the first response (with Idempotent-Replayed: true) and never sends twice. The same key with a different body is 422 idempotency_key_reused.
The 24-hour window#
Consent is always required. On top of it, to keep business messages welcome: within 24 hours of the user's last message to you, you can reply freely (subject to rate limits). Outside that window you can start at most 3 messages per user per 24 hours. GET /users/:appUserId tells you whether the window is open.
Delivery status#
GET /messages/:id returns the message and its status: sent β delivered β read. Subscribe to message.status to be told instead of polling. Messages the user sent you have status received.
Send errors#
| HTTP | code | Meaning |
|---|---|---|
| 400 | idempotency_key_required | Add an Idempotency-Key header. |
| 400 | invalid_request | The body failed validation; see error.details. |
| 403 | consent_required | The user has not opted in, or opted out, or blocked you. |
| 403 | recipient_not_tester | Sandbox: the user is not an accepted tester. |
| 403 | recipient_outside_org | Internal live: the user is not in your organization. |
| 403 | recipient_org_blocks_businesses | The user's organization doesn't allow outside businesses. |
| 403 | recipient_unavailable | The account is not active. |
| 403 | app_suspended | The app is suspended or rejected. |
| 403 | email_addressing_not_allowed | Email addressing needs internal live + a live key. |
| 404 | recipient_not_found | No such appUserId (or email) for this app. |
| 409 | media_not_uploaded | The media upload has not finished. |
| 422 | media_invalid | The uploaded file does not match what you declared. |
| 422 | idempotency_key_reused | Same key, different body. |
| 429 | daily_quota_exceeded | Daily message quota reached. |
| 429 | business_initiated_cap | More than 3 business-initiated messages to this user in 24 hours. |
| 429 | rate_limited | Too many requests; wait Retry-After seconds. |