Skip to main content
POST
/
v1
/
messages
/
bulk
Schedule a bulk template broadcast to many recipients
curl --request POST \
  --url https://api.keebai.com/v1/messages/bulk \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "phone_number_id": "100000000000001",
  "template_name": "welcome_v2",
  "language": "es",
  "recipients": [
    {
      "to": "+5491155555555",
      "variables": {
        "nombre": "Lucio"
      },
      "meta_data": {}
    }
  ],
  "campaign_name": "Onboarding marzo 2026"
}
'
{
  "broadcast_id": "<string>",
  "status": "scheduled",
  "total_recipients": 123,
  "scheduled_at": "<string>"
}
Send the same template to multiple recipients, with potentially different variables per recipient. Creates a broadcast in the database that you can track via GET /v1/messages/bulk/:broadcastId.

Endpoint

POST https://api.keebai.com/v1/messages/bulk

Required scope

messages:bulk
The messages:bulk scope is separate from messages:send precisely because bulk sending carries higher risk (Meta cost, sender number reputation, potential abuse). Grant it only to tokens controlled by the marketing tool or the job that needs to trigger campaigns.

Headers

HeaderRequiredValue
AuthorizationYesBearer kbai_pk_<token>
Content-TypeYesapplication/json

Body

FieldTypeRequiredDescription
template_namestringYesName of the approved template.
languagestringYesTemplate language code.
channel_idstringYesObjectId of the sending WhatsApp channel.
campaign_namestringNoHuman-readable name to identify the campaign in the portal. Auto-generated if omitted.
recipientsarrayYesBetween 1 and 5000 recipients. Each item: { to, variables?, meta_data? }.

Structure of each recipients[i]

FieldTypeRequiredDescription
tostringYesPhone number in E.164 format.
variablesobject<string, string>NoTemplate variables for this specific recipient.
meta_dataobjectNoFree-form metadata persisted with this individual send.

Example request

curl -X POST https://api.keebai.com/v1/messages/bulk \
  -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "promo_marzo_2026",
    "language": "es",
    "channel_id": "65a1f2b3c4d5e6f7a8b9c0d1",
    "campaign_name": "Promo marzo 2026 — clientes activos",
    "recipients": [
      { "to": "+5491155551111", "variables": { "nombre": "Lucio" } },
      { "to": "+5491155552222", "variables": { "nombre": "Camila" } },
      { "to": "+5491155553333", "variables": { "nombre": "Joaquín" } }
    ]
  }'

Response

202 Accepted

{
  "broadcast_id": "65f4b5c6d7e8f9a0b1c2d3e4",
  "status": "PENDING",
  "total_recipients": 3,
  "scheduled_at": null
}
FieldTypeDescription
broadcast_idstringBroadcast id. Store it to check status later.
statusstringInitial status: PENDING. Switches to IN_PROGRESS once processing starts.
total_recipientsnumberNumber of accepted recipients.
scheduled_atstring | nullSchedule date if applicable.

400 Bad Request

Invalid body: empty array, more than 5000 recipients, malformed phone number on any recipient.

401 Unauthorized

Missing, invalid, revoked, or expired token.

403 Forbidden

The token does not have the messages:bulk scope.

Best practices

  • Split very large batches. If your campaign has 50000 recipients, send 10 requests of 5000 each instead of fighting with timeouts. Use different campaign_name values to keep them distinct.
  • Deduplicate before sending. The API does not deduplicate by phone number — if you send the same recipient twice, the message goes out twice and Meta charges twice.
  • Keep variables consistent with the template. If the template expects {{nombre}} and {{monto}} and a recipient is missing monto, that individual send will fail. Validate the variable set up front.
  • Monitor status via GET /v1/messages/bulk/:broadcastId to detect campaigns with a high error rate.

Authorizations

Authorization
string
header
required

Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.

Body

application/json
phone_number_id
string
required

WhatsApp Business phone_number_id (numeric identifier issued by Meta for the sending number).

Example:

"100000000000001"

template_name
string
required
Example:

"welcome_v2"

language
string
required
Example:

"es"

recipients
object[]
required
campaign_name
string
Example:

"Onboarding marzo 2026"

Response

202 - application/json
broadcast_id
string
required
status
string
required
Example:

"scheduled"

total_recipients
number
required
scheduled_at
string | null