Messaging

Messages

Send text, media, templates and interactive messages; read a message, its delivery history and its files.

Live

Send a message

POST /v2/messages · scope messages:write · requires Idempotency-Key · live, unpinned key

Choose where (exactly one of to, contactId, conversationId) and what (text, plus at most one of mediaId, template, interactive):

Field
connectionIdThe connection to send from. Required. With conversationId it must be that conversation's connection (400 otherwise).
toA recipient: a phone number in E.164 (+447700900123) for WhatsApp, an email address for email. Reuses the person's open thread on this connection, wakes a resolved or snoozed one, or opens a new one.
contactIdAn existing contact, reached through their identity on this connection.
conversationIdReply into an existing conversation.
textThe message text, up to 4,096 characters. With mediaId it is the caption.
mediaIdA file uploaded with POST /v2/media.
templateAn approved WhatsApp template: { "id": "…" } or { "name": "…", "language": "en_US" }, plus variables by number ({ "1": "Layla" }). See Templates.
interactiveUp to 3 buttons ({ "type": "buttons", "body", "buttons": [{ "id", "title" }] }) or a list ({ "type": "list", "body", "button", "sections": [{ "title", "rows": [{ "id", "title", "description" }] }] }), where the connection supports them.
replyToQuote one of this thread's messages (its id).
metadataYour own labels, up to 20 string keys (40 characters) and values (500 characters). Stored on the message and returned with it.
curl https://app.gauge.chat/backend/v2/messages \
  -H "Authorization: Bearer $GAUGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3a7c0e52-61b9-4d1f-8e2a-9c5b7d3f1e08" \
  -d '{
    "conversationId": "c41e7b2a-5d6f-4e8a-9b1c-3d5e7f9a1b2c",
    "connectionId": "9b2f6c1e-3d4a-4e5b-8f70-1a2b3c4d5e6f",
    "text": "Thanks, Layla. Your refund is on its way.",
    "metadata": { "orderId": "A-1042" }
  }'

201 sent, or 202 queued

StatusMeansstatus in the body
201 CreatedThe channel accepted the message during your request.sent
202 AcceptedThe message is saved and will be dispatched in the background (WhatsApp, email, …). It is not sent yet.queued

Both answer the same body:

{
  "message": {
    "id": "7f3e9a1c-2b4d-4e6f-8a0b-1c2d3e4f5a6b",
    "conversationId": "c41e7b2a-5d6f-4e8a-9b1c-3d5e7f9a1b2c",
    "type": "message",
    "direction": "outbound",
    "authorType": "automation",
    "authorMemberId": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
    "kind": "text",
    "text": "Thanks, Layla. Your refund is on its way.",
    "status": "queued",
    "failureReason": null,
    "media": null,
    "template": null,
    "via": "api",
    "replyToMessageId": null,
    "metadata": { "orderId": "A-1042" },
    "createdAt": "2026-10-21T09:30:12.418Z"
  },
  "conversationId": "c41e7b2a-5d6f-4e8a-9b1c-3d5e7f9a1b2c",
  "status": "queued",
  "warnings": []
}

A 202 is a promise to try, not a delivery. Follow a queued message to sent (or failed) with the message.status_updated webhook, or poll GET /v2/messages/{id}. A message still queued after 30 minutes (26 hours on WhatsApp Web, which may hold a send for its daily cap) is marked failed. Retry a timed-out request with the same Idempotency-Key: it can never send twice (see Idempotency).

WhatsApp Web connections add "warnings": ["whatsapp_web_unofficial"] to every send: see WhatsApp Web. A WhatsApp Web number that is still warming up caps how many messages it sends, new chats included; over the cap a send answers 429.

Message someone first

With to or contactId instead of conversationId, a send opens the conversation if there is none (or reuses and reopens the one there is). On WhatsApp Cloud the first message must be an approved template; WhatsApp allows nothing else:

curl https://app.gauge.chat/backend/v2/messages \
  -H "Authorization: Bearer $GAUGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5d2b8e14-7a3c-4f9e-b1d6-0e8c2a4f6b39" \
  -d '{
    "connectionId": "9b2f6c1e-3d4a-4e5b-8f70-1a2b3c4d5e6f",
    "to": "+447700900123",
    "template": { "name": "appointment_reminder", "language": "en_GB", "variables": { "1": "Layla", "2": "Tuesday 22 October", "3": "10:30" } }
  }'

The answer carries the new conversationId. See Start a conversation with a template.

Read a message

GET /v2/messages/{id} · scope messages:read

The message object above. status is one of queued, sent, delivered, read, failed (outbound) or received (inbound). via is api for a message an API key sent; such a message has authorType: automation and authorMemberId set to the key's member. media is { mime, filename, mediaId } when it carries a file (mediaId only for a file you uploaded).

Download a message's file

GET /v2/messages/{id}/attachments/{index} · scope messages:read

A signed download link for a file on a message, one a customer sent or one you sent, valid for 5 minutes. This is the path a webhook's media[].url carries. A message carries one file today, so index is 0.

{
  "messageId": "7f3e9a1c-2b4d-4e6f-8a0b-1c2d3e4f5a6b",
  "index": 0,
  "contentType": "image/jpeg",
  "filename": "receipt.jpg",
  "sizeBytes": 183402,
  "download": { "url": "https://…", "expiresAt": "2026-10-21T09:35:12.000Z" }
}
AnswerWhy
404 not_foundNo such message for this key, no file at that index, or reason attachment_not_available: Gauge holds no copy of the file (for example a WhatsApp Web file that was never fetched)
503 media_unavailableFile downloads are not available on this deployment yet. Retry later.

Delivery history

GET /v2/messages/{id}/status-history · scope messages:read

{
  "messageId": "7f3e9a1c-2b4d-4e6f-8a0b-1c2d3e4f5a6b",
  "status": "delivered",
  "history": [
    { "status": "queued", "occurredAt": "2026-10-21T09:30:12.418Z", "failureReason": null },
    { "status": "sent", "occurredAt": "2026-10-21T09:30:13.020Z", "failureReason": null },
    { "status": "delivered", "occurredAt": "2026-10-21T09:30:15.771Z", "failureReason": null }
  ]
}

A message moves queued → sent → delivered → read, or ends failed with a failureReason. Not every channel reports every step (email has no read).

When a send is refused

AnswerreasonWhy
400 validation_failedA missing or conflicting field, an unknown template, or a conversation on another connection; details names the field
400 validation_failedmedia_not_uploadedThe mediaId exists but its file was never PUT
403 forbiddensandbox_key_not_supported, key_pinned_to_projectsTest keys and App-pinned keys cannot send
404 not_foundThe connection, contact or conversation does not exist or is not visible to this key
409 idempotency_mismatchThe Idempotency-Key was used with a different body
409 conflictidempotency_in_flightThe first request with this key is still running
413 payload_too_largefile_too_largeThe file is over the connection's limit for its type (details.maxBytes)
422 unsupported_channel_actionoutside_24h_windowFree text to WhatsApp more than 24 hours after the customer's last message, or to someone on WhatsApp Cloud who has no conversation yet: send a template
422 unsupported_channel_actiontemplate_not_approved, template_language_not_found, template_variable_mismatchThe template is not approved, not in that language, or variables is not exactly its {{1}}..{{n}} (Templates)
422 unsupported_channel_actionoutside_session_windowThe same, on Instagram or Messenger (their own reply window)
422 unsupported_channel_actionsend_not_supported, media_not_supported, templates_not_supported, interactive_not_supported, quoted_reply_not_supported, cannot_start_conversations, connection_inactiveThe connection cannot do this (see its capabilities on GET /v2/connections/{id})
429 rate_limitedwhatsapp_web_minute_cap, whatsapp_web_daily_capA WhatsApp Web number's warm-up cap; wait Retry-After seconds (caps)
429 rate_limitedYour key's or workspace's request rate; see Rate limits

On this page