API ReferenceSend Message (REST)

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/messages

Authenticate with a workspace API key:

Authorization: bcak_YOUR_API_KEY
Content-Type: application/json

Create 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

AttributeTypeDescription
workspaceIdnumber

Required. Workspace ID that owns the API key and channel.

integrationIdstring

Required. Channel (integration) ID to send from.

externalClientIdstring | 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.

typestring

Required. One of: 'text', 'image', 'audio', 'document', 'video', 'location', 'interactive', 'template', 'button', 'contacts'.

contentstring | object

Required for non-template types. For templates, either pass template JSON here (with name) or use templateName / templateComponents.

templateNamestring

Required for type=template when content.name is not provided. Approved template name for the channel.

templateLangstring

Optional. Template language code (e.g. en, en_US).

templateComponentsarray | object

Optional. Template components as sent to the channel (no server-side placeholder rewriting).

replyToMessageIdstring

Optional. Internal message ID to reply to. Only allowed for a single externalClientId (not bulk arrays).

Examples

Template (Cloud API content object)

request.json
{
  "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)

request.json
{
  "workspaceId": 12,
  "integrationId": "6743884f784839ad14770000",
  "externalClientId": "15551234567",
  "type": "template",
  "templateName": "otp_verification",
  "templateLang": "en",
  "templateComponents": [
    {
      "type": "body",
      "parameters": [{ "type": "text", "text": "482913" }]
    }
  ]
}

Text session message

request.json
{
  "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.

request.json
{
  "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)

response.json
{
  "messageId": "68875c39-01b8-42a5-9273-fcfd2f697600",
  "status": "pending",
  "type": "template",
  "createdAt": "2026-03-16T00:00:00.000Z"
}
AttributeTypeDescription
messageIdstring

Internal message ID.

statusstring

Accepted/queued status after enqueue (typically pending until the channel provider reports delivery).

typestring

Message type that was sent.

createdAtstring

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).

response.json
{
  "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
}
AttributeTypeDescription
resultsarray

One entry per unique recipient (after trim / de-dupe).

results[].externalClientIdstring

Destination ID that was processed.

results[].messageIdstring

Present on success. Internal message ID.

results[].statusstring

Present on success. Accepted/queued status after enqueue (typically pending).

results[].typestring

Present on success. Message type that was sent.

results[].createdAtstring

Present on success. Creation timestamp.

results[].errorstring

Present on failure for that recipient.

succeedednumber

Count of successful sends in this request.

failednumber

Count of failed sends in this request.

Error responses

HTTPMeaning
400Validation error (missing/invalid fields), or single-recipient send failure. Body: { "error": "..." }
401Missing Authorization header
402Insufficient Gateway credits for the full recipient count (INSUFFICIENT_CREDITS)
403Invalid API key, workspace mismatch, or not on Gateway plan (GATEWAY_PLAN_REQUIRED)
503Credits temporarily unavailable (CREDITS_UNAVAILABLE)
500Unexpected 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" }
          ]
        }
      ]
    }
  }'