Help centerAdminDecision maker

Outbound Dispatch API: send proactive messages from your backend

Queue WhatsApp/Telegram sends with the same worker as the Outbound page. Auth, single/batch send, Meta templates, opt-outs, limits, and receipts.

The Outbound Dispatch API lets your backend queue proactive messages on a workspace’s connected channels — the same queue, pacing, suppression list, and audit trail as the in-app Outbound page. Use it from CRMs, order systems, and schedulers when you need server-triggered sends at scale.

Base URL and auth

Base URL
https://concierge.bentokit.ai/api/integrations/concierge/v1/dispatch
  • Authorization: Bearer czsk_… (workspace API key from API Access → Keys)
  • Every call is scoped to that key’s workspace
  • Sends are asynchronous: validate + enqueue now; a paced worker delivers later
  • Optional Idempotency-Key header (or body idempotencyKey) dedupes retries
cURL skeleton
curl -sS -X POST "https://concierge.bentokit.ai/api/integrations/concierge/v1/dispatch" \
  -H "Authorization: Bearer czsk_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4958343-shipped" \
  -d '{ "channelId": "whatsapp-2", "to": "5521999999999", "text": "Olá!" }'

Console twin

  1. Create an API key

    API Access → Keys. Copy the secret once (czsk_…). Store it only on your server.

  2. Connect an outbound-capable channel

    WhatsApp Cloud, WhatsApp Personal, Telegram, etc. The channel must support proactive send (Outbound page shows ready routes).

  3. Rehearse in Outbound (optional)

    Compose a single send in the console first. The API uses the same worker and suppression list.

  4. Call the API from your backend

    Prefer approved provider templates for official WhatsApp Cloud. Free-form text works on channels that allow it.

List recent dispatches

GET / with optional status, batchId, and limit (1–500, default 50) returns recent dispatch rows for the workspace. Status values include queued, sending, accepted, sent, delivered, read, failed, blocked, expired, cancelled.

Send one message

POST / returns 202 with { dispatch } when queued, 409 when blocked (e.g. opted out), or { dispatch, deduped: true } when an idempotency key matches an existing row. Exactly one content shape is required.

POST body (one of three content shapes)
{
  "channelId": "whatsapp-2",
  "to": "5521999999999",
  "ttlSeconds": 86400,
  "text": "…",
  "templateId": "tpl_…",
  "variables": { "name": "Ana" },
  "providerTemplate": {
    "name": "order_update",
    "language": "pt_BR",
    "parameters": ["Ana", "4958343"]
  },
  "idempotencyKey": "optional-stable-key"
}
  • text: free-form (channels that allow free-form outbound)
  • templateId + variables: workspace saved message with {{named}} placeholders
  • providerTemplate: approved Meta-style template (required for official WhatsApp Cloud)
  • ttlSeconds: 60–604800 (default 86400). Expired-in-queue is discarded, never sent late
  • Omit channelId only when the workspace has exactly one outbound channel; otherwise pass it or you get channel_ambiguous

Batch send

POST /batch accepts the same content shapes with recipients: [{ to, variables? | parameters? }]. Up to 500 recipients per request. A tenant-scoped batch idempotency key prevents double-enqueue on retries.

POST /batch sketch
{
  "channelId": "whatsapp-2",
  "providerTemplate": {
    "name": "order_update",
    "language": "pt_BR",
    "parameters": ["PLACEHOLDER"]
  },
  "recipients": [
    { "to": "5521999999999", "parameters": ["Ana", "4958343"] },
    { "to": "5521888888888", "parameters": ["Bruno", "4958344"] }
  ],
  "ttlSeconds": 86400
}

Recipients from Concierge leads

GET /recipients?channelId=…&q=…&limit=… projects captured leads as sendable phone recipients for that channel (normalized id, name, phone, email, lead source/status, optedOut). Phone-kind channels only; other kinds return unsupportedKind: true until chat-id mapping exists.

Saved messages (workspace templates)

  • GET|POST /templates — list or create named bodies with {{variable}} placeholders
  • GET|PUT|DELETE /templates/:id — read, update, delete
  • Usable only on channels that allow free-form / saved-message outbound

Provider (Meta) templates

  • GET /channels/:channelId/templates[?includeUnavailable=true] — approved by default, or all statuses
  • POST /channels/:channelId/templates — submit a new template when the channel declares templateManagement
  • DELETE /channels/:channelId/templates/:name — delete all language versions for that name (provider semantics)
Submit a Meta UTILITY template
{
  "name": "order_update",
  "language": "pt_BR",
  "category": "UTILITY",
  "bodyText": "Olá {{1}}, seu pedido {{2}} está a caminho.",
  "bodyExamples": ["Ana", "4958343"],
  "footerText": "BentoKit"
}

Validation failures return 400 with stable error codes (invalid_template_name, template_placeholders_not_sequential, template_examples_mismatch, …). New templates are often status pending until Meta review finishes.

Suppression list (opt-outs)

  • GET /optouts — list suppressed recipients
  • POST /optouts — { to, channelId? | channelKind? } blocks before enqueue
  • DELETE /optouts?to=…&channelKind=… — remove a suppression
  • Honor the same list in your own systems so you do not re-target people who opted out

Limits and errors

  • Body text max ~4000 characters
  • Batch max 500 recipients
  • Default daily dispatch cap 1000/workspace (env CONCIERGE_DISPATCH_DAILY_CAP) → 429 daily_dispatch_cap
  • Enqueue burst limit (default 120/min) → 429 rate_limited
  • Common 4xx: channel_not_found, no_dispatch_channel, channel_ambiguous, template_or_text_required, provider_template_not_approved, recipient_opted_out (blocked)
  • 402 plan_feature_missing when outbound_campaigns is not on the plan

Delivery receipts back to you

If you need your system to learn when a message was accepted, delivered, or failed, subscribe to dispatch.* on API Access → Webhooks (optional). Most CRM integrations can skip those events and poll GET / instead.

  • API key stored only on the server
  • Official WhatsApp uses approved providerTemplate only
  • Idempotency-Key on every production send that may retry
  • Suppression list checked or mirrored in your CRM
  • TTL set so late channel outages do not send stale promos