curl --request POST \
--url https://api.orbit.devotel.io/api/v1/contacts/bulk/send \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"contact_ids": [
"cnt_9f27b1",
"cnt_4c11a8"
],
"name": "July re-engagement blast",
"channel": "sms",
"message_template": "Hi {{first_name}} — your 20% renewal code is ready."
}
'import requests
url = "https://api.orbit.devotel.io/api/v1/contacts/bulk/send"
payload = {
"contact_ids": ["cnt_9f27b1", "cnt_4c11a8"],
"name": "July re-engagement blast",
"channel": "sms",
"message_template": "Hi {{first_name}} — your 20% renewal code is ready."
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
contact_ids: ['cnt_9f27b1', 'cnt_4c11a8'],
name: 'July re-engagement blast',
channel: 'sms',
message_template: 'Hi {{first_name}} — your 20% renewal code is ready.'
})
};
fetch('https://api.orbit.devotel.io/api/v1/contacts/bulk/send', 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.orbit.devotel.io/api/v1/contacts/bulk/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'contact_ids' => [
'cnt_9f27b1',
'cnt_4c11a8'
],
'name' => 'July re-engagement blast',
'channel' => 'sms',
'message_template' => 'Hi {{first_name}} — your 20% renewal code is ready.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.orbit.devotel.io/api/v1/contacts/bulk/send"
payload := strings.NewReader("{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.orbit.devotel.io/api/v1/contacts/bulk/send")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.orbit.devotel.io/api/v1/contacts/bulk/send")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {},
"meta": {}
}{
"data": {},
"meta": {}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "<string>",
"status": 429,
"retry_after": 2
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}Send an ad-hoc broadcast to a selected audience
Launch an ad-hoc broadcast campaign to a selected audience. Pick the audience with exactly one of contact_ids (explicit list, up to 50,000) or filters (the same AST as segment filters). The request creates a real campaign — opt-outs, billing, throttling and DLR tracking apply exactly as for scheduled campaigns. Small audiences start inline and answer 201; larger ones are queued to the scheduler and answer 202 with a job_id to poll via GET /contacts/bulk/jobs/{id}.
curl --request POST \
--url https://api.orbit.devotel.io/api/v1/contacts/bulk/send \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"contact_ids": [
"cnt_9f27b1",
"cnt_4c11a8"
],
"name": "July re-engagement blast",
"channel": "sms",
"message_template": "Hi {{first_name}} — your 20% renewal code is ready."
}
'import requests
url = "https://api.orbit.devotel.io/api/v1/contacts/bulk/send"
payload = {
"contact_ids": ["cnt_9f27b1", "cnt_4c11a8"],
"name": "July re-engagement blast",
"channel": "sms",
"message_template": "Hi {{first_name}} — your 20% renewal code is ready."
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
contact_ids: ['cnt_9f27b1', 'cnt_4c11a8'],
name: 'July re-engagement blast',
channel: 'sms',
message_template: 'Hi {{first_name}} — your 20% renewal code is ready.'
})
};
fetch('https://api.orbit.devotel.io/api/v1/contacts/bulk/send', 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.orbit.devotel.io/api/v1/contacts/bulk/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'contact_ids' => [
'cnt_9f27b1',
'cnt_4c11a8'
],
'name' => 'July re-engagement blast',
'channel' => 'sms',
'message_template' => 'Hi {{first_name}} — your 20% renewal code is ready.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.orbit.devotel.io/api/v1/contacts/bulk/send"
payload := strings.NewReader("{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.orbit.devotel.io/api/v1/contacts/bulk/send")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.orbit.devotel.io/api/v1/contacts/bulk/send")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"contact_ids\": [\n \"cnt_9f27b1\",\n \"cnt_4c11a8\"\n ],\n \"name\": \"July re-engagement blast\",\n \"channel\": \"sms\",\n \"message_template\": \"Hi {{first_name}} — your 20% renewal code is ready.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {},
"meta": {}
}{
"data": {},
"meta": {}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "<string>",
"status": 429,
"retry_after": 2
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"status": 123,
"details": {}
},
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"docs_url": "<string>"
}
}Authorizations
Dashboard JWT token from Clerk
Headers
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
1 - 255Sandbox opt-in for Clerk-session-authenticated requests — see Test mode explained for the full credential-header matrix and worked samples. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.
true, false Body
Explicit contact ID list; mutually exclusive with filters.
Segment filter AST selecting the audience; mutually exclusive with contact_ids.
Name of the created broadcast campaign (max 200 chars).
Channel the message is sent on.
sms, whatsapp, rcs, viber, email, messenger, instagram, voice Message body delivered to every resolved recipient (max 4,096 chars).
Subject line; used for email broadcasts (max 500 chars).
Optional ISO 8601 datetime; when set the broadcast starts then instead of immediately.