Send Message (REST)
Overview
Gateway plan only. This endpoint works only for workspaces on the Gateway plan.
Non-Gateway (inbox) plans receive 403 GATEWAY_PLAN_REQUIRED and should use GraphQL
Messaging (addMessage / sendNotification) instead.
Send an outbound message through the Gateway REST API on any connected channel. Messages are accepted, persisted as pending, and pushed to an outbound queue for channel delivery (same pattern as inbound external → core, opposite direction). Destination is always externalClientId (shared across channels). Sends consume prepaid Gateway credits.
Pass externalClientId as a string for one recipient, or as a string array for bulk send (up to 1000 recipients). Bulk is best-effort: each recipient is processed independently and the response lists per-recipient success or error.
Async processing: The API returns immediately after accepting and queuing messages (typically < 100ms). Messages are then processed in the background sequentially per channel with rate limiting to comply with provider limits. For WhatsApp channels on the default tier (80 MPS), 1,000 messages to a single channel will take approximately 14 seconds to deliver after the API response. Multiple channels process in parallel.
POST https://gateway.bcrumbs.net/core/v1/messagesAuthenticate with a workspace API key:
Authorization: bcak_YOUR_API_KEY
Content-Type: application/jsonCreate API keys in the portal under Management → API Keys. The key must belong to the same workspaceId you send in the body. The workspace must be on the Gateway plan.
Channel rules still apply. For WhatsApp, Meta may reject non-template traffic outside an open 24-hour customer-care window — use type: “template” with an approved template when there is no open session.
Request body
| Attribute | Type | Description |
|---|---|---|
workspaceId | number | Required. Workspace ID that owns the API key and channel. |
integrationId | string | Required. Channel (integration) ID to send from. |
externalClientId | string | string[] | Required. One destination ID, or an array of up to 1000 destination IDs for bulk send (e.g. WhatsApp phone digits, Telegram chat id). Same field for every channel. Empty strings are rejected; duplicates are ignored. |
type | string | Required. One of: 'text', 'image', 'audio', 'document', 'video', 'location', 'interactive', 'template', 'button', 'contacts'. |
content | string | object | Required for non-template types. For templates, either pass template JSON here (with name) or use templateName / templateComponents. |
templateName | string | Required for type=template when content.name is not provided. Approved template name for the channel. |
templateLang | string | Optional. Template language code (e.g. en, en_US). |
templateComponents | array | object | Optional. Template components as sent to the channel (no server-side placeholder rewriting). |
replyToMessageId | string | Optional. Internal message ID to reply to. Only allowed for a single externalClientId (not bulk arrays). |
Examples
Template (Cloud API content object)
{
"workspaceId": 12,
"integrationId": "6743884f784839ad14770000",
"externalClientId": "15551234567",
"type": "template",
"content": {
"name": "order_update",
"language": { "code": "en_US" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Ada Lovelace" },
{ "type": "text", "text": "ORD-9912" }
]
}
]
}
}Template (top-level fields)
{
"workspaceId": 12,
"integrationId": "6743884f784839ad14770000",
"externalClientId": "15551234567",
"type": "template",
"templateName": "otp_verification",
"templateLang": "en",
"templateComponents": [
{
"type": "body",
"parameters": [{ "type": "text", "text": "482913" }]
}
]
}Text session message
{
"workspaceId": 12,
"integrationId": "6743884f784839ad14770000",
"externalClientId": "15551234567",
"type": "text",
"content": "Hello from Bread Crumbs Gateway"
}Bulk send (array of externalClientId)
Same message body is sent to every recipient. Credits are reserved for the full recipient count up front.
{
"workspaceId": 12,
"integrationId": "6743884f784839ad14770000",
"externalClientId": ["15551234567", "15557654321", "15559876543"],
"type": "template",
"content": {
"name": "order_update",
"language": { "code": "en_US" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Customer" },
{ "type": "text", "text": "ORD-9912" }
]
}
]
}
}Response
Single recipient (externalClientId string)
{
"messageId": "68875c39-01b8-42a5-9273-fcfd2f697600",
"status": "pending",
"type": "template",
"createdAt": "2026-03-16T00:00:00.000Z"
}| Attribute | Type | Description |
|---|---|---|
messageId | string | Internal message ID. |
status | string | Accepted/queued status after enqueue (typically pending until the channel provider reports delivery). |
type | string | Message type that was sent. |
createdAt | string | Creation timestamp. |
Bulk (externalClientId array)
HTTP 200 with per-recipient results. Shared validation / plan / credit failures still return the error statuses below (request never starts).
{
"results": [
{
"externalClientId": "15551234567",
"messageId": "68875c39-01b8-42a5-9273-fcfd2f697600",
"status": "pending",
"type": "template",
"createdAt": "2026-03-16T00:00:00.000Z"
},
{
"externalClientId": "15557654321",
"error": "Channel rejected the message"
}
],
"succeeded": 1,
"failed": 1
}| Attribute | Type | Description |
|---|---|---|
results | array | One entry per unique recipient (after trim / de-dupe). |
results[].externalClientId | string | Destination ID that was processed. |
results[].messageId | string | Present on success. Internal message ID. |
results[].status | string | Present on success. Accepted/queued status after enqueue (typically pending). |
results[].type | string | Present on success. Message type that was sent. |
results[].createdAt | string | Present on success. Creation timestamp. |
results[].error | string | Present on failure for that recipient. |
succeeded | number | Count of successful sends in this request. |
failed | number | Count of failed sends in this request. |
Error responses
| HTTP | Meaning |
|---|---|
400 | Validation error (missing/invalid fields), or single-recipient send failure. Body: { "error": "..." } |
401 | Missing Authorization header |
402 | Insufficient Gateway credits for the full recipient count (INSUFFICIENT_CREDITS) |
403 | Invalid API key, workspace mismatch, or not on Gateway plan (GATEWAY_PLAN_REQUIRED) |
503 | Credits temporarily unavailable (CREDITS_UNAVAILABLE) |
500 | Unexpected server error |
Example when the workspace is not on Gateway:
{
"error": "GATEWAY_PLAN_REQUIRED",
"message": "GATEWAY_PLAN_REQUIRED: POST /core/v1/messages is only available on the Gateway plan."
}Programming language examples
curl -X POST 'https://gateway.bcrumbs.net/core/v1/messages' \
-H 'Authorization: bcak_YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": 12,
"integrationId": "6743884f784839ad14770000",
"externalClientId": ["15551234567", "15557654321"],
"type": "template",
"content": {
"name": "order_update",
"language": { "code": "en_US" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Ada Lovelace" },
{ "type": "text", "text": "ORD-9912" }
]
}
]
}
}'