Bulk sends API
Send an approved template to a segment with every safeguard. 7 ops: list_campaigns, get_campaign, create_campaign, estimate_campaign, send_campaign, pause_campaign, cancel_campaign.
Send an approved template to a segment with every safeguard.
list_campaigns
readList campaigns · POST /api/v1/list_campaigns · scope campaigns:read or GET …?input=
Campaigns, newest first, with status, template, segment and stats.
curl -X POST https://getwabi.dev/api/v1/list_campaigns \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_campaigns{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"draft",
"scheduled",
"sending",
"paused",
"completed",
"canceled",
"failed"
]
},
"limit": {
"default": 50,
"description": "Page size, at most 200.",
"type": "integer",
"minimum": 1,
"maximum": 200
},
"offset": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
}
}get_campaign
readGet a campaign · POST /api/v1/get_campaign · scope campaigns:read or GET …?input=
A campaign with its last estimate and live progress: recipients by status (pending, queued, excluded with reasons, refused, canceled) and messages sent, delivered, read and failed.
curl -X POST https://getwabi.dev/api/v1/get_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi get_campaign --json '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"campaign": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Campaign id."
}
},
"required": [
"campaign"
]
}create_campaign
writeCreate a campaign · POST /api/v1/create_campaign · scope campaigns:write
Creates a draft campaign: one approved template (name, language, components with the variable values) sent from one number to a segment (id or builtin:<name>). Marketing templates require granted consent for consent_purpose (default marketing; null only for utility templates the contacts expect). frequency_cap_hours skips contacts that got any campaign message that recently. Nothing is sent: call estimate_campaign (dry run and cost), then send_campaign.
curl -X POST https://getwabi.dev/api/v1/create_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"name":"Support","phone_number_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","segment":"segment","template":{"name":"Support","language":"es"}}'npx @meterapp/wabi create_campaign --json '{"name":"Support","phone_number_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","segment":"segment","template":{"name":"Support","language":"es"}}'{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"phone_number_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Connected sender id (whatsapp_phone_numbers.id, not Meta's phone_number_id)."
},
"segment": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "Segment id or builtin:<name> (list_segments)."
},
"template": {
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9_]{1,512}$"
},
"language": {
"type": "string",
"minLength": 2,
"maxLength": 15
},
"components": {
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"category": {
"description": "Only when the template is not in Templates; otherwise read from there.",
"type": "string",
"enum": [
"marketing",
"utility"
]
}
},
"required": [
"name",
"language"
]
},
"consent_purpose": {
"anyOf": [
{
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,63}$"
},
{
"type": "null"
}
]
},
"frequency_cap_hours": {
"default": 24,
"type": "integer",
"minimum": 0,
"maximum": 720
},
"max_recipients": {
"default": 10000,
"type": "integer",
"minimum": 1,
"maximum": 10000
},
"scheduled_at": {
"description": "Send at this time instead of at send_campaign.",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
}
},
"required": [
"name",
"phone_number_id",
"segment",
"template"
]
}Dry-run a campaign · POST /api/v1/estimate_campaign · scope campaigns:read or GET …?input=
Resolves the campaign's audience now without sending: how many would receive it, how many are excluded and why (opted_out, no_consent, consent_revoked, frequency_cap, duplicate, not_a_sandbox_contact, over_limit), sample recipients, and Meta's cost at the published per-message rates by market (shown at cost, billed by Meta to your WABA; the sandbox is free). Also says whether the plan allows the send.
curl -X POST https://getwabi.dev/api/v1/estimate_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi estimate_campaign --json '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"campaign": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Campaign id."
}
},
"required": [
"campaign"
]
}send_campaign
writeSend a campaign · POST /api/v1/send_campaign · scope campaigns:write
Starts a draft campaign (or schedules it when scheduled_at is in the future): freezes the eligible audience and sends in batches through the messaging engine, whose safeguards apply to every message. Needs confirm: true after a person approved the estimate. Live numbers need the Growth plan; the sandbox works on every plan. Returns the campaign; follow progress with get_campaign.
curl -X POST https://getwabi.dev/api/v1/send_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","confirm":false}'npx @meterapp/wabi send_campaign --json '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","confirm":false}'{
"type": "object",
"properties": {
"campaign": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Campaign id."
},
"confirm": {
"type": "boolean",
"const": true,
"description": "true after a person reviewed estimate_campaign."
}
},
"required": [
"campaign",
"confirm"
]
}pause_campaign
writePause or resume a campaign · POST /api/v1/pause_campaign · scope campaigns:write
Pauses a sending or scheduled campaign (messages already queued still go out) or resumes a paused one.
curl -X POST https://getwabi.dev/api/v1/pause_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","paused":false}'npx @meterapp/wabi pause_campaign --json '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10","paused":false}'{
"type": "object",
"properties": {
"campaign": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Campaign id."
},
"paused": {
"type": "boolean"
}
},
"required": [
"campaign",
"paused"
]
}cancel_campaign
writeCancel a campaign · POST /api/v1/cancel_campaign · scope campaigns:write
Cancels a campaign: recipients not yet sent to are skipped. Messages already queued may still go out.
curl -X POST https://getwabi.dev/api/v1/cancel_campaign \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi cancel_campaign --json '{"campaign":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"campaign": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Campaign id."
}
},
"required": [
"campaign"
]
}