Create → (Update) → Run → Follow progress → Completed → Archive / Delete
│
└→ Pause → ResumeDraft, or as Scheduled if you give it a date and time.Draft and Scheduled campaigns can be changed.Completed by itself.| What you need | What it looks like | Where to find it |
|---|---|---|
| Your API key | ext-... | Sent in the header Authorization: Bearer ext-<your-api-key> on every request. |
| A contact list with contacts in it | contact_list_id, for example 12 | GET /contact/list. The list must not be archived. |
| The WhatsApp phone number that will send the message | meta_integration_whatsapp_phone_number_id, for example 5 | GET /meta-integration |
| The message template | whatsapp_template_id, for example 40 | GET /meta-integration/{meta_integration_whatsapp_id}/whatsapp-template |
| The template's variables | whatsapp_template_dynamic_variable_id, for example 210 | In the same template response. A variable is a gap in the message, like {{1}} in "Hi {{1}}, your order is ready". |
| The kind of message | campaign_message_type_id, for example 1 | GET /global/campaign-message-type (no key needed). |
| The contact fields that fill the template gaps | contact_field_id, for example 2 for First Name | GET /contact/field?contact_list_id=12 |
| (Optional) tags, to send to only some contacts | contact_tag_id, for example 9 | GET /contact/tag?contact_list_id=12 |
POST /campaign{
"campaign_title": "October Offer",
"contact_list_id": 12,
"campaign_message_type_id": 1,
"flag_template": true,
"whatsapp_template_id": 40,
"meta_integration_whatsapp_phone_number_id": 5,
"campaign_message_content": "",
"campaign_message_sending_date": null,
"campaign_message_sending_time": null,
"contact_tag_id": [],
"campaign_variable": [
{
"contact_field_id": 2,
"whatsapp_template_dynamic_variable_id": 210,
"campaign_variable_name": "First Name",
"campaign_variable_fallback_value": "Customer"
}
],
"campaign_carousel_card": []
}| Line | In plain words | Required? |
|---|---|---|
campaign_title | The name of the campaign. It must be different from every other campaign you have. | Yes |
contact_list_id | The list of people who will get the message. | Yes |
campaign_message_type_id | The kind of message. Use an ID from the message types lookup. You can leave it out for a follow-up campaign (section 5). | Yes |
flag_template | Always true when you send a template. | Yes |
whatsapp_template_id | The template you want to send. | Yes |
meta_integration_whatsapp_phone_number_id | The WhatsApp number the message is sent from. | Yes |
campaign_message_content | Leave it as "" (empty) when you send a template. | Yes, keep "" |
campaign_message_sending_date | The day to send, as YYYY-MM-DD, for example "2030-12-31". Leave it null to send when you press run. | No |
campaign_message_sending_time | The time to send, as HH:MM in 24-hour time, for example "09:30". Leave it null to send when you press run. | No |
contact_tag_id | Send only to contacts with these tags. [] means everyone in the list. | No |
campaign_variable | Fills the gaps in the template. See 3.3. | Yes, if the template has gaps |
campaign_carousel_card | Only for carousel templates. See section 6. Otherwise leave []. | No |
waba_id. It isn't used.Hi {{1}}, your order {{2}} is ready.
{
"contact_field_id": 2,
"whatsapp_template_dynamic_variable_id": 210,
"campaign_variable_name": "First Name",
"campaign_variable_fallback_value": "Customer"
}whatsapp_template_dynamic_variable_id: which gap this block fills. Take it from the template details.contact_field_id: the contact field to read the text from. Each person's own value is used, so John gets "Hi John" and Asha gets "Hi Asha".campaign_variable_fallback_value: the text to use when a contact has nothing in that field, so nobody gets an empty gap.campaign_variable_name: a label for you. It doesn't change the message.contact_field_id, a fallback value, or both.contact_field_id and write only the fallback value."campaign_variable": [
{
"contact_field_id": 2,
"whatsapp_template_dynamic_variable_id": 210,
"campaign_variable_name": "First Name",
"campaign_variable_fallback_value": "Customer"
},
{
"whatsapp_template_dynamic_variable_id": 211,
"campaign_variable_name": "Order Number",
"campaign_variable_fallback_value": "your order"
}
]"campaign_message_sending_date": "2030-12-31",
"campaign_message_sending_time": "09:30"Scheduled and is started automatically at that time. You don't need to press run.YYYY-MM-DD and HH:MM. Fill both or neither.null. It becomes a Draft again."contact_tag_id": [9, 14]campaign_id: write this number down. You need it to update, run, pause and archive the campaign.campaign_execution_status_name: Draft or Scheduled.total_campaign_contact: how many contacts will get the message.campaign_contact_assignment_data: the list of contacts in the campaign, each with the message prepared for them, so you can check who will get it before you run.PUT /campaignDraft or Scheduled."campaign_id": 123. Whatever you send replaces what was saved.campaign_variable_id (you can see it in Get campaign). Use 0 for a new block. A block you leave out is removed.campaign_carousel_card_id. A card you leave out is removed.campaign_contact_filter, or send [], the filters are removed."retargeted_from_campaign_id": 123,
"campaign_contact_filter_logic_operator": "OR",
"campaign_contact_filter": [
{
"campaign_contact_filter_type_id": 1,
"campaign_contact_filter_type_name": "Campaign Execution Status",
"campaign_contact_filter_type_data": [
{ "campaign_contact_filter_unique_id": 4, "campaign_contact_filter_unique_value": [] },
{ "campaign_contact_filter_unique_id": 8, "campaign_contact_filter_unique_value": [] }
]
}
]| Line | In plain words |
|---|---|
retargeted_from_campaign_id | The earlier campaign whose contacts you want to pick from. |
campaign_contact_filter_type_id | The kind of filter. Take it from GET /global/campaign-contact-filter-type. "Campaign Execution Status" filters by what happened to the message. "Contact Field" filters by a value in a contact's details. |
campaign_contact_filter_unique_id | For "Campaign Execution Status": the ID of the status, such as Read or Failed, from GET /global/campaign-execution-status. For "Contact Field": the contact_field_id. |
campaign_contact_filter_unique_value | For "Campaign Execution Status": always []. For "Contact Field": the values to match, for example ["Mumbai", "Pune"]. |
campaign_contact_filter_logic_operator | Put it once, at the top of the body, next to campaign_title. Use "OR" or "AND", as explained below. |
"OR": a contact is picked if it matches any of your statuses. Example: Delivered or Failed."AND" (the default): a contact must match all of your statuses at once. Example: Delivered and Read."OR".campaign_message_type_id."campaign_carousel_card": [
{
"whatsapp_template_carousel_variation_id": 501,
"campaign_carousel_card_index": 0,
"campaign_variable": [
{
"contact_field_id": 2,
"whatsapp_template_dynamic_variable_id": 330,
"campaign_variable_name": "First Name",
"campaign_variable_fallback_value": "Customer"
}
]
},
{
"whatsapp_template_carousel_variation_id": 502,
"campaign_carousel_card_index": 1,
"campaign_variable": []
}
]campaign_carousel_card_index is the position of the card: 0 for the first, 1 for the second, and so on.campaign_variable works exactly like section 3.3, but only for that card's gaps. Check the template details to see which gaps belong to which card.POST /campaign/run-campaign{
"campaign_id": 123
}Draft or Scheduled campaign (starts it) and for a Paused campaign (continues it).{ "status": true }. The messages are sent in the background, so the campaign takes a while to finish. Follow the progress with Get campaign (section 8).Opted Out.Message Undelivered.Completed.GET /campaign?campaign_id=123| Field | What it tells you |
|---|---|
campaign_execution_status_name | Where the campaign is: Draft, Scheduled, In Progress, Paused, Completed or Archived. |
total_campaign_contact | How many contacts the campaign has. |
total_sent_campaign_contact | Messages sent. |
total_delivered_campaign_contact | Messages that reached the phone. |
total_read_campaign_contact | Messages that were read. |
total_pending_campaign_contact | Contacts still waiting. |
total_failed_campaign_contact | Messages that failed. |
total_message_undelivered_campaign_contact | Contacts skipped because they can't receive messages. |
total_opted_out_campaign_contact | Contacts skipped because they opted out. |
total_contacts_who_clicked | People who clicked a button or link. |
total_contacts_who_replied | People who replied. |
campaign_contact_assignment_data | One entry per contact, with that contact's own status and the reason if it failed (failure_reason). |
campaign_id, GET /campaign gives a short summary of all your campaigns. GET /campaign/multiple-campaign gives combined numbers for your Completed campaigns. You can add campaign_ids=1,2 and start_date / end_date (for example 2026-01-01) to narrow it down.| Action | Request | Body | When you can do it |
|---|---|---|---|
| Pause | POST /campaign/pause-campaign | { "campaign_id": 123, "pause_reason": "Fixing the message" } | Only while the campaign is In Progress. pause_reason is optional. |
| Resume | POST /campaign/resume-campaign | { "campaign_id": 123 } | Only while it is Paused. It carries on with the contacts not yet sent. |
| Archive | POST /campaign/archive-campaign | { "campaign_id": 123 } | Any campaign except one that is In Progress or already Archived. |
| Delete | DELETE /campaign?campaign_id=123 | none: the number goes in the web address | Only Draft or Scheduled campaigns. |
| Status | Meaning | What you can do |
|---|---|---|
| Draft | Created, not sent, no schedule. | Update, run, delete, archive |
| Scheduled | Created with a date and time. It starts by itself. | Update, run now, delete, archive |
| In Progress | Messages are being sent. | Pause |
| Paused | Stopped, by you or by the system. | Resume, archive |
| Completed | Every contact has been handled. | Archive |
| Archived | Hidden away. | Nothing |
campaign_contact_assignment_data)| Status | Meaning |
|---|---|
| Pending | Waiting to be sent. |
| Sent | Sent from your WhatsApp number. |
| Delivered | Reached the person's phone. |
| Read | The person opened it. |
| Failed | WhatsApp could not send it. The reason is in failure_reason. |
| Message Undelivered | Skipped, because this number can't receive messages. |
| Opted Out | Skipped, because the person chose to stop receiving marketing messages. |
| Clicked / Replied | The person clicked a button or link, or replied. |
{ "status": false, "error": { "code": 9001, "message": "..." } }.| Code | What happened | What to do |
|---|---|---|
9001 Invalid body | Something in your request is missing or wrong. | Check the required lines in section 3.2. Check the date and time format. Check that the list, template and phone number IDs exist, that every gap has a variable block, and that the campaign is in the right status for what you're doing. |
9436 Campaign name already exists | Another campaign has this title. | Use a different campaign_title. |
9303 Authentication templates can't be used | You picked a login-code template. | Pick a marketing or utility template. |
9330 Contact list is archived | The list is archived. | Use another list, or make the list Active again. |
9032 Data not found | The campaign has no contacts. | Add contacts to the list and create the campaign again. |
9244 The contact list has been updated | Contacts were deleted after the campaign was created. | Delete the campaign and create a new one. |
9245 Cannot resume | Same reason, while resuming. | Create a new campaign. |
9246 Organization is paused | An admin paused your organization. | Contact your admin. |
9002 Invalid query parameter | The number in the web address is missing or wrong, or the campaign can't be deleted in its current status. | Check campaign_id, and that the campaign is Draft or Scheduled. |
401 Invalid or expired API key | The key is wrong or has expired. | Create a new key. |
403 Access not granted | Your key isn't allowed to use this API. | Ask your admin. |
campaign_contact_assignment_data and the contacts in it are the ones you want.total_campaign_contact is what you expect.campaign_id.