Introduction
This documentation aims to provide all the information you need to work with our API.
<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_API_TOKEN}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Issue a token from /app/integrations/api-tokens and pass it as Authorization: Bearer .... Tokens carry abilities (read / write / send / admin) — endpoints reject calls that lack the required ability.
Analytics
Read the organization's aggregated daily metrics (conversations, messages, agents and social insights) as totals per metric key.
Metrics summary
requires authentication
Returns totals per metric key over a date range (inclusive). Defaults
to the last 30 days when from/to are omitted. Pass platform to
scope to a single social platform.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/metrics?from=2026-06-01&to=2026-06-30&platform=telegram" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-08-18T12:48:29\",
\"to\": \"2026-08-18T12:48:29\",
\"platform\": \"b\"
}"
const url = new URL(
"http://localhost/api/v1/analytics/metrics"
);
const params = {
"from": "2026-06-01",
"to": "2026-06-30",
"platform": "telegram",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-08-18T12:48:29",
"to": "2026-08-18T12:48:29",
"platform": "b"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"from": "2026-06-01",
"to": "2026-06-30",
"platform": null,
"metrics": {
"messages.inbound": 1200,
"messages.outbound": 940,
"conversations.opened": 310,
"social.followers": 4820
}
}
}
Example response (422):
{
"message": "The from field must be a valid date.",
"errors": {
"from": [
"The from field must be a valid date."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Campaigns
Broadcast (bulk / drip) message campaigns inside the authenticated organization. Create a draft, then queue it to materialize its audience.
List campaigns
requires authentication
Returns a paginated list of campaigns, newest first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/campaigns?status=draft&per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/campaigns"
);
const params = {
"status": "draft",
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8CMP1",
"name": "Ramadan promo",
"type": "bulk",
"status": "draft",
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"template_id": null,
"audience_size": 0,
"queued_count": 0,
"sent_count": 0,
"delivered_count": 0,
"read_count": 0,
"failed_count": 0,
"scheduled_at": null,
"started_at": null,
"finished_at": null,
"created_at": "2026-07-04T08:00:00+00:00"
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a campaign
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/campaigns/01HZK7P5R3Q6V0YH4XJ3M8CMP1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/campaigns/01HZK7P5R3Q6V0YH4XJ3M8CMP1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
{
"message": "No query results for model [Campaign]."
}
Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a draft campaign
requires authentication
Creates a campaign in the draft state. The audience is not
materialized until you call the queue endpoint. audience_filter
accepts contact ULIDs and/or tag slugs; contact ULIDs are resolved to
the recipients selected at queue time.
Example request:
curl --request POST \
"http://localhost/api/v1/campaigns" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Ramadan promo\",
\"channel_id\": \"01HZK7P5R3Q6V0YH4XJ3M8CHN1\",
\"template_id\": \"01HZK7P5R3Q6V0YH4XJ3M8TPL1\",
\"type\": \"bulk\",
\"audience_filter\": {
\"contact_ids\": [
\"01HZK7P5R3Q6V0YH4XJ3M8AAA1\"
],
\"tag_slugs\": [
\"vip\"
]
}
}"
const url = new URL(
"http://localhost/api/v1/campaigns"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Ramadan promo",
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"template_id": "01HZK7P5R3Q6V0YH4XJ3M8TPL1",
"type": "bulk",
"audience_filter": {
"contact_ids": [
"01HZK7P5R3Q6V0YH4XJ3M8AAA1"
],
"tag_slugs": [
"vip"
]
}
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8CMP1",
"name": "Ramadan promo",
"type": "bulk",
"status": "draft",
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"template_id": null,
"audience_size": 0,
"queued_count": 0,
"created_at": "2026-07-04T08:00:00+00:00"
}
}
Example response (404):
{
"message": "No query results for model [Channel]."
}
Example response (422):
{
"message": "The name field is required.",
"errors": {
"name": [
"The name field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Queue a campaign
requires authentication
Materializes the campaign's audience into recipients and moves it to
the running state. Only draft and scheduled campaigns can be
queued; other states are returned unchanged.
Example request:
curl --request POST \
"http://localhost/api/v1/campaigns/01HZK7P5R3Q6V0YH4XJ3M8CMP1/queue" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/campaigns/01HZK7P5R3Q6V0YH4XJ3M8CMP1/queue"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8CMP1",
"name": "Ramadan promo",
"status": "running",
"audience_size": 120,
"queued_count": 120
}
}
Example response (404):
{
"message": "No query results for model [Campaign]."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Channels
Read-only listing of the channels connected to the active organization. Provisioning happens in the admin UI; the API only surfaces the connected channels so callers can pick one for outbound sends.
List channels
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/channels?type=whatsapp&status=connected" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/channels"
);
const params = {
"type": "whatsapp",
"status": "connected",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"type": "cloud",
"status": "connected",
"display_name": "Sales line",
"phone_number": "+966500000000",
"connected_at": "2026-05-01T08:00:00+00:00",
"last_seen_at": "2026-05-06T10:21:00+00:00"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a channel
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/channels/01HZK7P5R3Q6V0YH4XJ3M8CHN1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/channels/01HZK7P5R3Q6V0YH4XJ3M8CHN1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete a channel
requires authentication
Disconnects the channel (stopping any live WhatsApp gateway session),
emits a channel.deleted webhook, then removes it. Use this to clean up
stale channels — e.g. old WhatsApp links left "awaiting scan" — without
opening the dashboard. Requires the write (or admin) ability.
Example request:
curl --request DELETE \
"http://localhost/api/v1/channels/01HZK7P5R3Q6V0YH4XJ3M8CHN1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/channels/01HZK7P5R3Q6V0YH4XJ3M8CHN1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (200):
{
"deleted": true
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Contacts
Manage WhatsApp contacts inside the authenticated organization.
List contacts
requires authentication
Returns a paginated list of contacts in the active organization, ordered by most recently created first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/contacts?search=ali&per_page=50&page=1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/contacts"
);
const params = {
"search": "ali",
"per_page": "50",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"wa_id": "966500000000",
"phone": "+966500000000",
"name": "Ali",
"email": null,
"language": "ar",
"last_seen_at": "2026-05-06T10:21:00+00:00",
"created_at": "2026-05-01T08:00:00+00:00"
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a contact
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/contacts/01HZK7P5R3Q6V0YH4XJ3M8AAA1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/contacts/01HZK7P5R3Q6V0YH4XJ3M8AAA1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
{
"message": "No query results for model [Contact]."
}
Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create or update a contact
requires authentication
Idempotent on (organization, wa_id). Existing contact attributes are overwritten by the values you send — omit a field to leave it untouched.
Example request:
curl --request POST \
"http://localhost/api/v1/contacts" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"wa_id\": \"966500000000\",
\"phone\": \"+966500000000\",
\"name\": \"Ali\",
\"email\": \"ali@example.com\",
\"language\": \"ar\",
\"profile\": [],
\"custom_fields\": []
}"
const url = new URL(
"http://localhost/api/v1/contacts"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"wa_id": "966500000000",
"phone": "+966500000000",
"name": "Ali",
"email": "ali@example.com",
"language": "ar",
"profile": [],
"custom_fields": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"wa_id": "966500000000",
"phone": "+966500000000",
"name": "Ali",
"email": null,
"language": "ar"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Conversations
List conversations
requires authentication
Sorted by last_message_at descending so the freshest activity
appears first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/conversations?status=open&channel_id=01HZK7P5R3Q6V0YH4XJ3M8CHN1&per_page=25" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/conversations"
);
const params = {
"status": "open",
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8CONV1",
"status": "open",
"priority": "normal",
"unread_count": 2,
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"contact_id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"last_message_at": "2026-05-06T10:21:00+00:00"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a conversation
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/conversations/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/conversations/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List messages in a conversation
requires authentication
Newest first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/conversations/architecto/messages?per_page=50" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/conversations/architecto/messages"
);
const params = {
"per_page": "50",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Email broadcasts
Bulk email — compose a broadcast to an audience of CRM contacts, then queue it. The audience is every contact in your organisation that has an email address and hasn't opted out; an optional tag filter narrows it. Each recipient is sent through the same pipeline as transactional email, so the suppression list is always honoured.
List email broadcasts
requires authentication
Returns the org's broadcasts, newest first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails/broadcasts?status=draft&per_page=25" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/broadcasts"
);
const params = {
"status": "draft",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a broadcast
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails/broadcasts/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/broadcasts/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a broadcast
requires authentication
Creates a draft broadcast. Queue it with POST /emails/broadcasts/{ulid}/queue.
Example request:
curl --request POST \
"http://localhost/api/v1/emails/broadcasts" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"July newsletter\",
\"from\": \"Acme <hello@mail.acme.com>\",
\"subject\": \"What\'s new in July\",
\"html\": \"<h1>Hello!<\\/h1>\",
\"text\": \"Hello!\",
\"audience\": {
\"tag_slugs\": [
\"newsletter\"
]
},
\"scheduled_at\": \"2026-07-20T09:00:00Z\"
}"
const url = new URL(
"http://localhost/api/v1/emails/broadcasts"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "July newsletter",
"from": "Acme <hello@mail.acme.com>",
"subject": "What's new in July",
"html": "<h1>Hello!<\/h1>",
"text": "Hello!",
"audience": {
"tag_slugs": [
"newsletter"
]
},
"scheduled_at": "2026-07-20T09:00:00Z"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": "01H...",
"status": "draft",
"name": "July newsletter"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Queue a broadcast
requires authentication
Materialises the audience and fans out one send per recipient. Returns
the broadcast with its updated status and queued_count.
Example request:
curl --request POST \
"http://localhost/api/v1/emails/broadcasts/architecto/queue" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/broadcasts/architecto/queue"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Emails
Transactional email — send programmatic email (receipts, OTPs, notifications) as one of your verified sending domains, and read the send log. Inbound replies land in the unified inbox, not here.
List sent emails
requires authentication
Returns the org's transactional send log, newest first.
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails?status=sent&per_page=50" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails"
);
const params = {
"status": "sent",
"per_page": "50",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Email delivery analytics
requires authentication
Returns a status-count summary (with delivery/bounce rates) plus a
per-day series over a date range (inclusive). Defaults to the last 30
days when from/to are omitted. The range is clamped to 366 days.
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails/analytics?from=2026-06-01&to=2026-06-30" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-08-18T12:48:29\",
\"to\": \"2026-08-18T12:48:29\"
}"
const url = new URL(
"http://localhost/api/v1/emails/analytics"
);
const params = {
"from": "2026-06-01",
"to": "2026-06-30",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-08-18T12:48:29",
"to": "2026-08-18T12:48:29"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"from": "2026-06-01",
"to": "2026-06-30",
"summary": {
"total": 120,
"queued": 0,
"sent": 100,
"delivered": 90,
"bounced": 5,
"complained": 1,
"failed": 4,
"delivery_rate": 75,
"bounce_rate": 4.17
},
"series": [
{
"date": "2026-06-01",
"sent": 4,
"delivered": 3,
"bounced": 0
}
]
}
}
Example response (422):
{
"message": "The from field must be a valid date.",
"errors": {
"from": [
"The from field must be a valid date."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a sent email
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send an email
requires authentication
Sends an email as from (which must be on a verified sending domain for
your organisation) and records it in the send log. The message is
DKIM-signed with the domain's key.
Example request:
curl --request POST \
"http://localhost/api/v1/emails" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"Acme <hello@mail.acme.com>\",
\"to\": [
\"ali@example.com\"
],
\"subject\": \"Your receipt\",
\"html\": \"<h1>Thanks!<\\/h1>\",
\"text\": \"Thanks!\",
\"template\": \"order-receipt\",
\"variables\": {
\"order_id\": \"1042\"
},
\"cc\": [
\"architecto\"
],
\"bcc\": [
\"architecto\"
],
\"reply_to\": \"support@mail.acme.com\",
\"headers\": []
}"
const url = new URL(
"http://localhost/api/v1/emails"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "Acme <hello@mail.acme.com>",
"to": [
"ali@example.com"
],
"subject": "Your receipt",
"html": "<h1>Thanks!<\/h1>",
"text": "Thanks!",
"template": "order-receipt",
"variables": {
"order_id": "1042"
},
"cc": [
"architecto"
],
"bcc": [
"architecto"
],
"reply_to": "support@mail.acme.com",
"headers": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": "01H...",
"status": "sent",
"subject": "Your receipt"
}
}
Example response (404):
{
"message": "Email template not found."
}
Example response (422):
{
"message": "No verified sending domain owns the From address ..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Email suppressions
The org's email suppression list — addresses we won't mail (hard bounces, complaints, or manual entries). Managed automatically by delivery events; this surface lets you read it and add/remove entries.
List suppressed addresses
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/emails/suppressions?reason=bounce&per_page=50" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/suppressions"
);
const params = {
"reason": "bounce",
"per_page": "50",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Suppress an address
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/emails/suppressions" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"address\": \"bounced@example.com\"
}"
const url = new URL(
"http://localhost/api/v1/emails/suppressions"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"address": "bounced@example.com"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Remove a suppression
requires authentication
Removes by suppression id (ULID) or by the raw address.
Example request:
curl --request DELETE \
"http://localhost/api/v1/emails/suppressions/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/emails/suppressions/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Email templates
Reusable email templates (subject/html/text with {{ variable }}
placeholders) an org can send from via the Send API by referencing a
template slug or ULID plus a variables map.
List email templates
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/email-templates?per_page=25" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/email-templates"
);
const params = {
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get an email template
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/email-templates/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/email-templates/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create an email template
requires authentication
slug is auto-derived from name when omitted. At least one of
subject/html/text should be supplied for the template to be
useful, but none are required at creation time.
Example request:
curl --request POST \
"http://localhost/api/v1/email-templates" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Order receipt\",
\"slug\": \"order-receipt\",
\"subject\": \"Your order {{ order_id }} is confirmed\",
\"html\": \"architecto\",
\"text\": \"architecto\",
\"variables\": []
}"
const url = new URL(
"http://localhost/api/v1/email-templates"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Order receipt",
"slug": "order-receipt",
"subject": "Your order {{ order_id }} is confirmed",
"html": "architecto",
"text": "architecto",
"variables": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": "01H...",
"name": "Order receipt",
"slug": "order-receipt"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update an email template
requires authentication
Only the supplied fields are changed. slug may be changed explicitly
but is never re-derived automatically on update.
Example request:
curl --request PATCH \
"http://localhost/api/v1/email-templates/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"architecto\",
\"slug\": \"architecto\",
\"subject\": \"architecto\",
\"html\": \"architecto\",
\"text\": \"architecto\",
\"variables\": []
}"
const url = new URL(
"http://localhost/api/v1/email-templates/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "architecto",
"slug": "architecto",
"subject": "architecto",
"html": "architecto",
"text": "architecto",
"variables": []
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete an email template
requires authentication
Example request:
curl --request DELETE \
"http://localhost/api/v1/email-templates/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/email-templates/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Embed widget
Server-side API for the embed SDK. The host app (e.g. a Laravel back-end) calls this to mint a short-lived signed token for the current visitor, then hands the token to the browser, which passes it to OktaWa.boot({ token }).
The token carries the visitor's external reference + optional context claims, so the embedded widget knows who it's talking to without ever exposing the org-side signing secret to the browser.
Mint a signed widget token
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/embed-tokens" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"slug\": \"marketing-site\",
\"subject\": \"user-12345\",
\"context\": [],
\"ttl_seconds\": 900
}"
const url = new URL(
"http://localhost/api/v1/embed-tokens"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"slug": "marketing-site",
"subject": "user-12345",
"context": [],
"ttl_seconds": 900
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"token": "eyJpc3MiOi...",
"expires_in": 900,
"slug": "marketing-site"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Endpoints
POST api/webhooks/email/inbound
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/email/inbound" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/email/inbound"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/email/inbound/ses
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/email/inbound/ses" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/email/inbound/ses"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/email/events
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/email/events" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/email/events"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/email/open/{ulid}/{signature}
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/email/open/architecto/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/email/open/architecto/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
Show headers
content-type: image/gif
cache-control: max-age=0, must-revalidate, no-cache, no-store, private
pragma: no-cache
expires: 0
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
GIF89a � !� , D ;
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/telegram/{ulid}
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/telegram/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/telegram/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/webhooks/instagram
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/webhooks/instagram" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/instagram"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (403):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
forbidden
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/instagram
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/instagram" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/instagram"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/webhooks/twitter
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/webhooks/twitter" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/twitter"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (400):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"error": "missing crc_token"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/twitter
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/twitter" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/twitter"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/tiktok
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/tiktok" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/tiktok"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/integrations/meta/config
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/integrations/meta/config" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/integrations/meta/config"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/integrations/meta/embedded-signup
requires authentication
Example request:
curl --request POST \
"http://localhost/api/integrations/meta/embedded-signup" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"code\": \"b\",
\"waba_id\": \"n\"
}"
const url = new URL(
"http://localhost/api/integrations/meta/embedded-signup"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"code": "b",
"waba_id": "n"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/integrations/qr/sessions
requires authentication
Example request:
curl --request POST \
"http://localhost/api/integrations/qr/sessions" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"display_name\": \"b\"
}"
const url = new URL(
"http://localhost/api/integrations/qr/sessions"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"display_name": "b"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/integrations/qr/sessions/{ulid}
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/integrations/qr/sessions/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/integrations/qr/sessions/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhooks/neoleap
requires authentication
Example request:
curl --request POST \
"http://localhost/api/webhooks/neoleap" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/webhooks/neoleap"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/groups
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/groups" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/groups"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/groups/{ulid}
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/groups/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/groups/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/v1/groups
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/groups" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"subject\": \"b\",
\"participants\": [
\"n\"
],
\"channel_id\": 16
}"
const url = new URL(
"http://localhost/api/v1/groups"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subject": "b",
"participants": [
"n"
],
"channel_id": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
PATCH api/v1/groups/{ulid}
requires authentication
Example request:
curl --request PATCH \
"http://localhost/api/v1/groups/architecto" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"subject\": \"b\",
\"description\": \"Et animi quos velit et fugiat.\"
}"
const url = new URL(
"http://localhost/api/v1/groups/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subject": "b",
"description": "Et animi quos velit et fugiat."
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/v1/groups/{ulid}/participants
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/groups/architecto/participants" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"participants\": [
\"b\"
]
}"
const url = new URL(
"http://localhost/api/v1/groups/architecto/participants"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"participants": [
"b"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
DELETE api/v1/groups/{ulid}/participants
requires authentication
Example request:
curl --request DELETE \
"http://localhost/api/v1/groups/architecto/participants" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"participants\": [
\"b\"
]
}"
const url = new URL(
"http://localhost/api/v1/groups/architecto/participants"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"participants": [
"b"
]
};
fetch(url, {
method: "DELETE",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
PUT api/v1/groups/{ulid}/picture
requires authentication
Example request:
curl --request PUT \
"http://localhost/api/v1/groups/architecto/picture" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"picture_base64\": \"bngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtnnoqi\"
}"
const url = new URL(
"http://localhost/api/v1/groups/architecto/picture"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"picture_base64": "bngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtnnoqi"
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/v1/groups/{ulid}/sync
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/groups/architecto/sync" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/groups/architecto/sync"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Messages
Public API surface for sending messages from external apps.
The integrating app provides a destination wa_id (or the ulid of an existing conversation) and the message body. We resolve / create the Contact + Conversation as needed, then hand off to SendMessageAction which queues the actual provider call.
Send a message
requires authentication
Send a free-form text or media message to a wa_id (auto-creates the
contact + conversation if needed) or into an existing conversation
via conversation_id. The message is queued; the response carries
the queued message resource — poll GET /conversations/{id}/messages
or subscribe to a webhook to track delivery status.
Example request:
curl --request POST \
"http://localhost/api/v1/messages" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"channel_id\": \"01HZK7P5R3Q6V0YH4XJ3M8CHN1\",
\"conversation_id\": \"01HZK7P5R3Q6V0YH4XJ3M8CONV1\",
\"wa_id\": \"966500000000\",
\"type\": \"text\",
\"body\": \"Your order is on the way!\",
\"media_url\": \"https:\\/\\/cdn.example.com\\/file.pdf\"
}"
const url = new URL(
"http://localhost/api/v1/messages"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"conversation_id": "01HZK7P5R3Q6V0YH4XJ3M8CONV1",
"wa_id": "966500000000",
"type": "text",
"body": "Your order is on the way!",
"media_url": "https:\/\/cdn.example.com\/file.pdf"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8MSG1",
"conversation_id": "01HZK7P5R3Q6V0YH4XJ3M8CONV1",
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"direction": "outbound",
"type": "text",
"body": "Your order is on the way!",
"status": "queued",
"created_at": "2026-05-06T10:21:00+00:00"
}
}
Example response (404):
{
"message": "Channel not found."
}
Example response (422):
{
"message": "The channel id field is required when conversation id is not present.",
"errors": {
"channel_id": [
"..."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
OAuth
Exchanges a one-time authorization code from the Connect screen (/connect → user authorizes) for a Sanctum personal access token the external app can then use as Authorization: Bearer ...
The flow is intentionally simpler than full OAuth2: no client_secret, no PKCE. Security relies on:
- Codes are one-time (used_at flag).
- Codes expire in 5 minutes.
- Codes are bound to a redirect_uri at issue time and the host must send the same redirect_uri at exchange time, so an attacker who intercepts the redirect URL still can't redeem the code without also impersonating the registered redirect target.
Exchange code for token
requires authentication
Example request:
curl --request POST \
"http://localhost/api/v1/oauth/token" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"code\": \"4f9e2c...\",
\"redirect_uri\": \"https:\\/\\/crm.example.com\\/oktawa\\/callback\"
}"
const url = new URL(
"http://localhost/api/v1/oauth/token"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"code": "4f9e2c...",
"redirect_uri": "https:\/\/crm.example.com\/oktawa\/callback"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"access_token": "1|abc...",
"token_type": "Bearer",
"abilities": [
"read",
"send"
]
}
}
Example response (400):
{
"error": "invalid_grant",
"message": "Authorization code is invalid, used, or expired."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Introspect the current connection
requires authentication
Returns the abilities granted to the calling token plus the app name, the workspace it's bound to, and expiry — so a connected app can check what it may do and decide whether it needs to request more. To add abilities, send the user back through the Connect screen (build the URL with the FULL desired ability set); the consent page highlights what's new. No request body.
Example request:
curl --request GET \
--get "http://localhost/api/v1/oauth/introspect" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/oauth/introspect"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"app_name": "Frameo",
"abilities": [
"read",
"send"
],
"organization": {
"id": "01HZK...",
"name": "Acme"
},
"expires_at": "2026-10-01T00:00:00+00:00",
"last_used_at": "2026-07-15T10:00:00+00:00"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Revoke the current connection
requires authentication
Lets a connected app disconnect ITSELF: called with the app's own bearer
token, it revokes that token (so all further calls 401) and fires a
connection.revoked webhook to the workspace. Idempotent — a token can
only be presented while it still exists. No request body.
Example request:
curl --request POST \
"http://localhost/api/v1/oauth/revoke" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/oauth/revoke"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200):
{
"revoked": true
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Partner API
The integration surface for a technical partner (شريك تقني): an account whose only job is wiring your own product into Connect. Through it you create workspaces for your customers, add and manage their users, mint their API tokens, reserve channels, and drop a user straight into the dashboard — all with keys you hold yourself.
This is not the platform-operator surface. A partner token can only see workspaces it provisioned (anything else answers 404, never 403), and no endpoint here mints an administrative grant over the platform.
Mint your keys from /app/partner once your partner account is approved. Full reference: docs/PARTNER_API.md.
Exchange keys for a token
The only unauthenticated endpoint on the Partner API — the key pair in the body IS the credential. Rate limit: 10/min.
Every rejection answers the same opaque invalid_client regardless of
cause, so the endpoint cannot be used to probe which client ids exist.
A suspended partner gets 403 partner_suspended.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/token" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"grant_type\": \"client_credentials\",
\"client_id\": \"okc_ci_01hzk7p5r3q6v0yh4xj3m8ws01\",
\"client_secret\": \"okc_cs_9f2c...\",
\"abilities\": [
\"workspaces.read\"
]
}"
const url = new URL(
"http://localhost/api/v1/partner/token"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"grant_type": "client_credentials",
"client_id": "okc_ci_01hzk7p5r3q6v0yh4xj3m8ws01",
"client_secret": "okc_cs_9f2c...",
"abilities": [
"workspaces.read"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"access_token": "okc_pat_...",
"token_type": "Bearer",
"expires_in": 3600,
"expires_at": "2026-08-18T12:00:00+00:00",
"abilities": [
"workspaces.read",
"workspaces.write"
]
}
Example response (401):
{
"error": "invalid_client"
}
Example response (403):
{
"error": "partner_suspended"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Who am I
requires authentication
Introspection: the partner this token belongs to, what it may do, when it expires, and how many workspaces you own. Call it on boot to confirm a token is live before running a provisioning sequence.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/me" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/me"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"partner": {
"id": "01HZK...",
"name": "Acme Technologies",
"slug": "acme",
"status": "active"
},
"token": {
"name": "exchange:integration key",
"kind": "exchanged",
"abilities": [
"workspaces.read",
"workspaces.write"
],
"expires_at": "2026-08-18T12:00:00+00:00"
},
"workspaces_count": 12
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List workspaces
requires authentication
Only workspaces you provisioned. Requires workspaces.read.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/workspaces?search=acme&external_id=acct_8891&status=active&per_page=25" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces"
);
const params = {
"search": "acme",
"external_id": "acct_8891",
"status": "active",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 119
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a workspace
requires authentication
Requires workspaces.read.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
{
"error": "workspace_not_found"
}
Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 118
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a workspace
requires authentication
Requires workspaces.write. Answers 201 on creation and 200 when an
external_id you already used returns the existing workspace — so a
retry never mints a duplicate tenant.
Pass owner to install the workspace owner in the same call;
password_auto returns a one_time_password on this response only.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Acme Support\",
\"slug\": \"acme-support\",
\"external_id\": \"acct_8891\",
\"locale\": \"ar\",
\"timezone\": \"Asia\\/Riyadh\",
\"country\": \"SA\",
\"metadata\": [],
\"owner\": {
\"name\": \"Sara Admin\",
\"email\": \"sara@acme.test\",
\"password\": \"|]|{+-\",
\"password_auto\": true
}
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Acme Support",
"slug": "acme-support",
"external_id": "acct_8891",
"locale": "ar",
"timezone": "Asia\/Riyadh",
"country": "SA",
"metadata": [],
"owner": {
"name": "Sara Admin",
"email": "sara@acme.test",
"password": "|]|{+-",
"password_auto": true
}
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update a workspace
requires authentication
Requires workspaces.write. The slug is immutable once issued —
integrations key their own records off it.
Example request:
curl --request PATCH \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Acme Support\",
\"locale\": \"en\",
\"timezone\": \"Asia\\/Riyadh\",
\"country\": \"SA\",
\"metadata\": []
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Acme Support",
"locale": "en",
"timezone": "Asia\/Riyadh",
"country": "SA",
"metadata": []
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Suspend a workspace
requires authentication
Your kill switch for an account that churned inside your product.
Requires workspaces.write.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/suspend" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/suspend"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Reactivate a workspace
requires authentication
Requires workspaces.write.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/activate" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/activate"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List workspace members
requires authentication
Requires users.read.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 117
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add a member
requires authentication
Requires users.write. Answers 201 for a newly created user and 200
when the email already belongs to a Connect account — that account is
reused as a member and its password is never overwritten.
Adding someone as owner transfers workspace ownership to them.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Khalid Agent\",
\"email\": \"khalid@acme.test\",
\"password\": \"|]|{+-\",
\"password_auto\": true,
\"locale\": \"ar\",
\"role\": \"agent\"
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Khalid Agent",
"email": "khalid@acme.test",
"password": "|]|{+-",
"password_auto": true,
"locale": "ar",
"role": "agent"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update a member
requires authentication
Requires users.write. The workspace owner cannot be suspended — hand
ownership to someone else first.
Example request:
curl --request PATCH \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users/01HZK7P5R3Q6V0YH4XJ3M8USR01" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"role\": \"manager\",
\"status\": \"suspended\"
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users/01HZK7P5R3Q6V0YH4XJ3M8USR01"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"role": "manager",
"status": "suspended"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (422):
{
"error": "owner_cannot_be_suspended"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Remove a member
requires authentication
Requires users.write. Drops the membership; the user account itself
survives, since it may belong to workspaces you know nothing about. The
owner cannot be removed — transfer ownership first.
Example request:
curl --request DELETE \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users/01HZK7P5R3Q6V0YH4XJ3M8USR01" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users/01HZK7P5R3Q6V0YH4XJ3M8USR01"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (200):
{
"deleted": true
}
Example response (422):
{
"error": "owner_cannot_be_removed"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List workspace tokens
requires authentication
Metadata only — no secrets. Requires tokens.write.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 116
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Mint a workspace token
requires authentication
Requires tokens.write. The plain token is returned exactly once —
Connect keeps only a hash.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Acme product sync\",
\"user_id\": \"01HZK7P5R3Q6V0YH4XJ3M8USR01\",
\"abilities\": [
\"read\",
\"send\"
],
\"expires_at\": \"2026-12-01T00:00:00Z\"
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Acme product sync",
"user_id": "01HZK7P5R3Q6V0YH4XJ3M8USR01",
"abilities": [
"read",
"send"
],
"expires_at": "2026-12-01T00:00:00Z"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Revoke a workspace token
requires authentication
Requires tokens.write.
Example request:
curl --request DELETE \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens/123" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens/123"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (200):
{
"revoked": true
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List workspace channels
requires authentication
Requires channels.read.
Example request:
curl --request GET \
--get "http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 115
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Reserve a channel
requires authentication
Requires channels.write. Creates the channel disconnected, with no
provider credentials attached.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"display_name\": \"Sales line\",
\"type\": \"cloud_api\",
\"phone_number\": \"+966500000000\"
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"display_name": "Sales line",
"type": "cloud_api",
"phone_number": "+966500000000"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Issue a one-time sign-in link
requires authentication
Send the member to the returned url and they land signed in, on the
workspace you named. Requires sso.issue.
The ticket is opaque and random, single-use (redeeming deletes it), and expires in five minutes. Membership is re-checked at redemption, so a member you removed in the meantime cannot use it.
Example request:
curl --request POST \
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/sso" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"user_id\": \"01HZK7P5R3Q6V0YH4XJ3M8USR01\",
\"redirect\": \"\\/app\\/inbox\"
}"
const url = new URL(
"http://localhost/api/v1/partner/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/sso"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"user_id": "01HZK7P5R3Q6V0YH4XJ3M8USR01",
"redirect": "\/app\/inbox"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"url": "https://connect.getokta.io/partner/sso?ticket=...",
"expires_in": 300
}
}
Example response (422):
{
"error": "not_an_active_member"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Social posts
Compose a post and fan it out to one or more social channels (Telegram, X,
Instagram, …). A future scheduled_at schedules the post; otherwise it is
created as a draft.
List social posts
requires authentication
Returns a paginated list of social posts with their per-channel targets, newest first.
Get a social post
requires authentication
Schedule a social post
requires authentication
Composes a post and fans it out to the given channels. Every channel
must support feed/story publishing. With a future scheduled_at the
post is scheduled; without one it is created as a draft.
Tags
List the organization's tags and apply tags to a contact. Applying tags is idempotent — tag slugs that don't exist yet are created on the fly.
List tags
requires authentication
Returns a paginated list of tags, ordered by name.
Apply tags to a contact
requires authentication
Attaches the given tags to the contact, creating any that don't exist yet. Existing attachments are left untouched (idempotent). Returns the contact with its full tag list.
Templates
List templates
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/templates?status=APPROVED&language=ar" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/templates"
);
const params = {
"status": "APPROVED",
"language": "ar",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send a template message
requires authentication
Send a Meta-approved template to a wa_id with optional positional variables (1-indexed in Meta, 0-indexed in the array — variable 1 = $variables[0]).
Example request:
curl --request POST \
"http://localhost/api/v1/templates/send" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"channel_id\": \"01HZK7P5R3Q6V0YH4XJ3M8CHN1\",
\"wa_id\": \"966500000000\",
\"template_name\": \"order_ready\",
\"language\": \"ar\",
\"variables\": [
\"12345\",
\"120 SAR\"
]
}"
const url = new URL(
"http://localhost/api/v1/templates/send"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
"wa_id": "966500000000",
"template_name": "order_ready",
"language": "ar",
"variables": [
"12345",
"120 SAR"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8MSG2",
"type": "template",
"status": "queued"
}
}
Example response (404):
{
"message": "Template not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Tickets
Support tickets inside the authenticated organization. Open a ticket in a pipeline, then transition it between stages.
List tickets
requires authentication
Returns a paginated list of tickets, newest first. Filter by the current stage category or by pipeline.
Example request:
curl --request GET \
--get "http://localhost/api/v1/tickets?status=open&pipeline_id=01HZK7P5R3Q6V0YH4XJ3M8PIP1&per_page=25" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/tickets"
);
const params = {
"status": "open",
"pipeline_id": "01HZK7P5R3Q6V0YH4XJ3M8PIP1",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8TKT1",
"number": 1001,
"subject": "Order not delivered",
"description": null,
"priority": "normal",
"status": "open",
"stage_id": "01HZK7P5R3Q6V0YH4XJ3M8STG1",
"stage": "New",
"pipeline_id": "01HZK7P5R3Q6V0YH4XJ3M8PIP1",
"contact_id": null,
"assigned_user_id": null,
"created_at": "2026-07-04T08:00:00+00:00"
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a ticket
requires authentication
Example request:
curl --request GET \
--get "http://localhost/api/v1/tickets/01HZK7P5R3Q6V0YH4XJ3M8TKT1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/tickets/01HZK7P5R3Q6V0YH4XJ3M8TKT1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
{
"message": "No query results for model [Ticket]."
}
Example response (500):
Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
strict-transport-security: max-age=63072000; includeSubDomains; preload
access-control-allow-origin: *
{
"message": "Server Error"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Open a ticket
requires authentication
Opens a ticket in the given pipeline (or the organization's default pipeline) at its first stage, applying the active SLA policy.
Example request:
curl --request POST \
"http://localhost/api/v1/tickets" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"subject\": \"Order not delivered\",
\"description\": \"Customer says parcel never arrived.\",
\"pipeline_id\": \"01HZK7P5R3Q6V0YH4XJ3M8PIP1\",
\"contact_id\": \"01HZK7P5R3Q6V0YH4XJ3M8AAA1\",
\"priority\": \"high\"
}"
const url = new URL(
"http://localhost/api/v1/tickets"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subject": "Order not delivered",
"description": "Customer says parcel never arrived.",
"pipeline_id": "01HZK7P5R3Q6V0YH4XJ3M8PIP1",
"contact_id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"priority": "high"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8TKT1",
"number": 1001,
"subject": "Order not delivered",
"priority": "high",
"status": "open",
"stage_id": "01HZK7P5R3Q6V0YH4XJ3M8STG1",
"pipeline_id": "01HZK7P5R3Q6V0YH4XJ3M8PIP1"
}
}
Example response (404):
{
"message": "No query results for model [TicketPipeline]."
}
Example response (422):
{
"message": "No ticket pipeline is available.",
"errors": {
"pipeline_id": [
"No ticket pipeline is available."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Transition a ticket to another stage
requires authentication
Moves the ticket to another stage within its own pipeline. Resolving to
a resolved/closed category stamps the corresponding timestamp.
Example request:
curl --request POST \
"http://localhost/api/v1/tickets/01HZK7P5R3Q6V0YH4XJ3M8TKT1/transition" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"stage_id\": \"01HZK7P5R3Q6V0YH4XJ3M8STG2\",
\"note\": \"Escalated to logistics.\"
}"
const url = new URL(
"http://localhost/api/v1/tickets/01HZK7P5R3Q6V0YH4XJ3M8TKT1/transition"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"stage_id": "01HZK7P5R3Q6V0YH4XJ3M8STG2",
"note": "Escalated to logistics."
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8TKT1",
"status": "resolved",
"stage_id": "01HZK7P5R3Q6V0YH4XJ3M8STG2",
"resolved_at": "2026-07-04T09:00:00+00:00"
}
}
Example response (404):
{
"message": "No query results for model [TicketStage]."
}
Example response (422):
{
"message": "The stage id field is required.",
"errors": {
"stage_id": [
"The stage id field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Webhooks
Register outbound webhook subscriptions programmatically instead of adding
them by hand in the dashboard. Deliveries are POSTed to your URL with an
X-Okta-Signature: sha256=<hmac> header (HMAC-SHA256 of the raw body using
the subscription secret) plus X-Okta-Event and X-Okta-Delivery.
List webhook subscriptions
requires authentication
Returns the active organization's webhook subscriptions, newest first. The signing secret is never returned here — it is shown only once, at creation time.
Example request:
curl --request GET \
--get "http://localhost/api/v1/webhooks?per_page=50" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/webhooks"
);
const params = {
"per_page": "50",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"name": "Order events",
"url": "https://example.test/hooks/okta",
"events": [
"subscription.expired",
"channel.deleted"
],
"is_active": true,
"max_attempts": 5
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a webhook subscription
requires authentication
Registers a new endpoint. A signing secret is generated and returned in
the secret field of THIS response ONLY — store it now to verify the
X-Okta-Signature header; it can never be read again.
Example request:
curl --request POST \
"http://localhost/api/v1/webhooks" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Order events\",
\"url\": \"https:\\/\\/example.test\\/hooks\\/okta\",
\"events\": [
\"subscription.expired\",
\"channel.deleted\"
],
\"max_attempts\": 5
}"
const url = new URL(
"http://localhost/api/v1/webhooks"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Order events",
"url": "https:\/\/example.test\/hooks\/okta",
"events": [
"subscription.expired",
"channel.deleted"
],
"max_attempts": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
"name": "Order events",
"url": "https://example.test/hooks/okta",
"events": [
"subscription.expired"
],
"is_active": true,
"max_attempts": 5,
"secret": "shown-once-store-it-now"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete a webhook subscription
requires authentication
Example request:
curl --request DELETE \
"http://localhost/api/v1/webhooks/01HZK7P5R3Q6V0YH4XJ3M8AAA1" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/webhooks/01HZK7P5R3Q6V0YH4XJ3M8AAA1"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (200):
{
"deleted": true
}
Example response (404):
{
"message": "No query results for model [WebhookSubscription]."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.