Skip to main content
POST
/
v1
/
businesses
/
{businessId}
/
send
/
campaigns
Send Campaigns
curl --request POST \
  --url https://api.example.com/v1/businesses/{businessId}/send/campaigns
import requests

url = "https://api.example.com/v1/businesses/{businessId}/send/campaigns"

response = requests.post(url)

print(response.text)
const options = {method: 'POST'};

fetch('https://api.example.com/v1/businesses/{businessId}/send/campaigns', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/businesses/{businessId}/send/campaigns",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.example.com/v1/businesses/{businessId}/send/campaigns"

req, _ := http.NewRequest("POST", url, nil)

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://api.example.com/v1/businesses/{businessId}/send/campaigns")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.example.com/v1/businesses/{businessId}/send/campaigns")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body

Endpoint

POST https://api.connectly.ai/v1/businesses/{businessId}/send/campaigns
ParameterLocationDescription
businessIdPathYour Connectly business ID.
X-API-KeyHeaderYour API key.
Campaigns must be published in the Connectly Flow Builder before you call this endpoint. Sending to an unpublished campaign returns a 409 error.

Request body

{
  "entries": [...],
  "options": {...}
}

entries array (required)

Each object in entries represents one recipient. You can mix entries for different campaigns in the same request.
FieldTypeRequiredDescription
clientstringYesRecipient’s WhatsApp number in E.164 format (e.g. +16505551234), or a BSUID-prefixed identifier (e.g. bsuid:US.13491208655302741918).
campaignNamestringYesExact campaign name copied from the Flow Builder.
variablesobjectNoKey-value pairs substituted into the flow’s variable placeholders for this recipient.
campaignVersionstringNoSpecific campaign version to target. If omitted, resolved by options.if_version_unspecified.
scheduledAtstringNoISO 8601 datetime for future delivery (e.g. "2024-03-15T10:00:00Z"). Alpha β€” contact your Account Manager before using.

options object (optional)

FieldValuesDescription
if_version_unspecifiedreuse_last_activeHow to resolve the version when campaignVersion is not set. reuse_last_active targets the latest published version (default).
if_duplicate_check_unspecifiedallow_one / allow_multipleallow_one (default) prevents sending the same campaign to the same customer twice. Set allow_multiple to override this β€” for example, for recurring service alerts.

Examples

The smallest valid entry. Use this for campaigns that have no variable placeholders.
{
  "entries": [
    {
      "client": "+16505551236",
      "campaignName": "campaign_basic"
    }
  ]
}
Variables are defined in the campaign flow in the Connectly UI. Pass the same key names here.
{
  "entries": [
    {
      "client": "+16505551237",
      "campaignName": "campaign_with_variables",
      "variables": {
        "username": "Jane Doe",
        "product": "Smart Watch"
      }
    }
  ]
}
Connectly creates a separate sendout for each unique campaignName + campaignVersion combination. This request produces three sendouts: campaign_A at v1.0, campaign_A at its latest active version, and campaign_B at v1.0.
{
  "entries": [
    {
      "client": "+14155558234",
      "campaignName": "campaign_A",
      "campaignVersion": "v1.0",
      "variables": { "key1": "value1", "key2": "value2" }
    },
    {
      "client": "+14155559345",
      "campaignName": "campaign_A",
      "variables": { "key1": "value3", "key2": "value4" }
    },
    {
      "client": "+14155550456",
      "campaignName": "campaign_B",
      "campaignVersion": "v1.0",
      "variables": { "Date": "2024-03-15", "Name": "Alice", "Price": "$99" }
    }
  ],
  "options": {
    "if_version_unspecified": "reuse_last_active"
  }
}
By default, the API prevents sending the same campaign to the same customer twice. Override with allow_multiple for use cases like recurring alerts.
{
  "entries": [
    {
      "client": "+14155558234",
      "campaignName": "service_alert",
      "variables": { "incident": "Scheduled maintenance" }
    }
  ],
  "options": {
    "if_duplicate_check_unspecified": "allow_multiple"
  }
}

Response

The API always returns HTTP 200 with a data array. Each element corresponds to a unique campaignName + campaignVersion sendout generated by the request.
FieldTypeDescription
campaignIdstringUnique identifier for the campaign.
campaignNamestringName of the campaign.
campaignVersionstringVersion used for this sendout.
sendoutIdstringUnique identifier for the sendout.
statusstringcreated, updated, or error.
acceptedCountintegerNumber of entries accepted into the sendout.
rejectedCountintegerNumber of entries rejected.
errorobject | nullError detail if status is error, otherwise null.
If you call the API again for the same campaign and recipient group, status returns "updated" rather than "created" β€” no duplicate sendout is created.
Success response
{
  "data": [
    {
      "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
      "campaignName": "campaign_basic",
      "campaignVersion": "018c5c51-8631-28b5-3c81-b70ecf14faef",
      "sendoutId": "183e801b-1438-4177-b283-909135096e69",
      "status": "created",
      "acceptedCount": 1,
      "rejectedCount": 0,
      "error": null
    }
  ]
}

Error responses

Entry-level errors appear inside the data array with status: "error" β€” they don’t cause the whole request to fail. HTTP-level errors indicate request-wide failures.
HTTP statusMeaning
400Request body is malformed or an entry is invalid.
401Missing or invalid API key.
404One or more referenced campaigns do not exist.
409Campaign is not in a state that accepts sendouts (e.g. not yet published).
429Rate limit exceeded.
500Internal server error.
{
  "data": [
    {
      "campaignId": null,
      "campaignName": "wrong_name",
      "campaignVersion": null,
      "sendoutId": null,
      "status": "error",
      "acceptedCount": 0,
      "rejectedCount": 1,
      "error": {
        "message": "Campaign not found",
        "type": "ERROR_TYPE_NOT_FOUND",
        "code": "ERROR_CODE_CAMPAIGN_NOT_FOUND",
        "userTitle": "Campaign not found",
        "userMessage": "Please review the campaign name and/or campaign id."
      }
    }
  ]
}
{
  "data": [
    {
      "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
      "campaignName": "campaign_basic",
      "campaignVersion": "018c4a55-f3df-580c-1629-602f3b14d190",
      "sendoutId": null,
      "status": "error",
      "acceptedCount": 0,
      "rejectedCount": 1,
      "error": {
        "message": "Campaign version not found",
        "type": "ERROR_TYPE_NOT_FOUND",
        "code": "ERROR_CODE_CAMPAIGN_VERSION_NOT_FOUND",
        "userTitle": "Campaign version not found",
        "userMessage": "Please review the campaign version."
      }
    }
  ]
}
Triggered when required flow variables are absent or the entry payload is otherwise invalid.
{
  "data": [
    {
      "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
      "campaignName": "campaign_with_variables",
      "campaignVersion": "018c5c51-8631-28b5-3c81-b70ecf14faef",
      "sendoutId": "183e801b-1438-4177-b283-909135096e69",
      "status": "error",
      "acceptedCount": 0,
      "rejectedCount": 1,
      "error": {
        "message": "Campaign entry is invalid",
        "type": "ERROR_TYPE_INVALID_REQUEST",
        "code": "ERROR_CODE_CAMPAIGN_ENTRY_INVALID",
        "userTitle": "Campaign entry is invalid",
        "userMessage": "Please check the inputs to the campaign entry."
      }
    }
  ]
}

Rate limiting

This endpoint is limited to 200 requests per second. Exceeding this returns HTTP 429 Too Many Requests. Use exponential backoff in your client if you expect high-volume sendouts.