MENU navbar-image

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.

Admin / Workspace channels

Reserve a channel slot

requires authentication

Creates a Channel row in disconnected state. Real provider credentials (Cloud API access token, QR pairing session, etc.) are wired up later via the dashboard or channel-specific endpoint; this just lets the SDK list the channel ahead of time.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"display_name\": \"Sales line\",
    \"phone_number\": \"+966500000000\",
    \"type\": \"architecto\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/channels"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "display_name": "Sales line",
    "phone_number": "+966500000000",
    "type": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/workspaces/{ulid}/channels

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Workspace ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8WS001

Body Parameters

display_name   string     

Human-friendly name. Example: Sales line

phone_number   string  optional    

Optional E.164 with leading +. Example: +966500000000

type   string     

One of cloud_api / baileys / embed. Example: architecto

Admin / Workspaces

Programmatic provisioning of Connect workspaces (Organizations) by a platform-admin token holder. Operates outside the per-tenant scope — the platform admin is the issuer of new tenants, not a member of any.

Create a workspace

requires authentication

Idempotent on slug: posting the same slug twice returns the existing workspace with HTTP 200 instead of failing the unique constraint, so installer modules can retry safely.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/workspaces" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Acme\",
    \"slug\": \"acme\",
    \"display_name\": \"Acme Corp.\",
    \"locale\": \"en\",
    \"metadata\": []
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/workspaces"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Acme",
    "slug": "acme",
    "display_name": "Acme Corp.",
    "locale": "en",
    "metadata": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/workspaces

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Display name. Example: Acme

slug   string     

URL-safe slug, lowercased, dashes only. Example: acme

display_name   string  optional    

Optional rich display name shown in dashboards. Example: Acme Corp.

locale   string  optional    

Two-letter locale, default ar. Example: en

metadata   object  optional    

Free-form metadata bag.

Update a workspace

requires authentication

Updates name, display_name, and metadata. The slug is intentionally immutable once issued — modules and installer tokens depend on a stable slug for routing.

Example request:
curl --request PATCH \
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"display_name\": \"n\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "display_name": "n"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PATCH api/v1/admin/workspaces/{ulid}

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Workspace ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8WS001

Body Parameters

name   string  optional    

Must not be greater than 120 characters. Example: b

display_name   string  optional    

Must not be greater than 160 characters. Example: n

metadata   object  optional    

Admin / Workspace tokens

Mint a Sanctum token for a workspace user

requires authentication

The plain-text token is returned exactly once. Persist it on the caller side immediately — Sanctum stores only a SHA-256 hash.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Connect installer token\",
    \"abilities\": [
        \"architecto\"
    ],
    \"user_id\": \"architecto\",
    \"expires_at\": \"2026-08-01T00:00:00Z\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/tokens"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Connect installer token",
    "abilities": [
        "architecto"
    ],
    "user_id": "architecto",
    "expires_at": "2026-08-01T00:00:00Z"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/workspaces/{ulid}/tokens

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Workspace ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8WS001

Body Parameters

name   string     

Human-readable label. Example: Connect installer token

abilities   string[]     

Subset of read,write,send,admin.

user_id   string     

ULID of the workspace user owning the token. Example: architecto

expires_at   string  optional    

Optional ISO 8601 expiration. Example: 2026-08-01T00:00:00Z

Admin / Workspace users

Create a user in a workspace

requires authentication

Creates a new User and attaches them to the workspace as admin (or the role you specify). If password_auto: true the action generates a strong password and returns it ONCE in data.one_time_password — Sanctum-style: not persisted in plain.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Sara Admin\",
    \"email\": \"admin@acme.test\",
    \"password\": \"|]|{+-\",
    \"password_auto\": false,
    \"role\": \"architecto\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/workspaces/01HZK7P5R3Q6V0YH4XJ3M8WS001/users"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Sara Admin",
    "email": "admin@acme.test",
    "password": "|]|{+-",
    "password_auto": false,
    "role": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/workspaces/{ulid}/users

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Workspace ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8WS001

Body Parameters

name   string     

User's full name. Example: Sara Admin

email   string     

Unique email. Example: admin@acme.test

password   string  optional    

Min 12 chars. Required unless password_auto is true. Example: |]|{+-

password_auto   boolean  optional    

If true the server generates and returns the password once. Example: false

role   string  optional    

One of owner/admin/manager/agent/member. Defaults to admin. Example: architecto

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 "https://connect.getokta.io/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-07-04T21:15:52\",
    \"to\": \"2026-07-04T21:15:52\",
    \"platform\": \"b\"
}"
const url = new URL(
    "https://connect.getokta.io/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-07-04T21:15:52",
    "to": "2026-07-04T21:15:52",
    "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."
        ]
    }
}
 

Request      

GET api/v1/analytics/metrics

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

from   string  optional    

date Start date (YYYY-MM-DD). Defaults to 30 days ago. Example: 2026-06-01

to   string  optional    

date End date (YYYY-MM-DD). Defaults to today. Example: 2026-06-30

platform   string  optional    

Restrict to a platform (e.g. telegram, instagram, x). Example: telegram

Body Parameters

from   string  optional    

Must be a valid date. Example: 2026-07-04T21:15:52

to   string  optional    

Must be a valid date. Example: 2026-07-04T21:15:52

platform   string  optional    

Must not be greater than 64 characters. Example: b

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 "https://connect.getokta.io/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(
    "https://connect.getokta.io/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
    }
}
 

Request      

GET api/v1/campaigns

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status (draft, scheduled, queueing, running, completed, cancelled, failed). Example: draft

per_page   integer  optional    

Page size, 1-100. Defaults to 25. Example: 25

page   integer  optional    

Page number. Example: 1

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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."
        ]
    }
}
 

Request      

POST api/v1/campaigns

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Human-readable campaign name. Example: Ramadan promo

channel_id   string     

The sending channel ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8CHN1

template_id   string  optional    

A message template ULID to send. Example: 01HZK7P5R3Q6V0YH4XJ3M8TPL1

type   string  optional    

Campaign type: bulk|drip. Defaults to "bulk". Example: bulk

audience_filter   object  optional    

Audience selection.

contact_ids   string[]  optional    

Contact ULIDs to include.

tag_slugs   string[]  optional    

Tag slugs to include.

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 \
    "https://connect.getokta.io/api/v1/campaigns/01HZK7P5R3Q6V0YH4XJ3M8CMP1/queue" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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]."
}
 

Request      

POST api/v1/campaigns/{ulid}/queue

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

The campaign ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8CMP1

Channels

Read-only listing of the WhatsApp 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 "https://connect.getokta.io/api/v1/channels" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/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 (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"
        }
    ]
}
 

Request      

GET api/v1/channels

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

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 "https://connect.getokta.io/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(
    "https://connect.getokta.io/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
    }
}
 

Request      

GET api/v1/contacts

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

search   string  optional    

Filter by name, phone, or wa_id (substring). Example: ali

per_page   integer  optional    

Page size, between 1 and 100. Defaults to 25. Example: 50

page   integer  optional    

Page number. Defaults to 1. Example: 1

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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"
    }
}
 

Request      

POST api/v1/contacts

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

wa_id   string     

E.164 number without the leading +. Example: 966500000000

phone   string  optional    

Display phone (with +). Example: +966500000000

name   string  optional    

Display name. Example: Ali

email   string  optional    

Optional email. Example: ali@example.com

language   string  optional    

Two-letter language hint. Example: ar

profile   object  optional    

Free-form profile bag (max 4 KB).

custom_fields   object  optional    

Free-form custom fields bag.

Conversations

List conversations

requires authentication

Sorted by last_message_at descending so the freshest activity appears first.

Example request:
curl --request GET \
    --get "https://connect.getokta.io/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(
    "https://connect.getokta.io/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"
        }
    ]
}
 

Request      

GET api/v1/conversations

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status (open, pending, snoozed, closed). Example: open

channel_id   string  optional    

Filter by channel ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8CHN1

per_page   integer  optional    

1-100, defaults to 25. Example: 25

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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"
    }
}
 

Request      

POST api/v1/embed-tokens

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

slug   string     

The widget config slug. Example: marketing-site

subject   string     

Visitor reference (your own user id, session id, etc). Example: user-12345

context   object  optional    

Free-form context bag (max 4 KB serialized).

ttl_seconds   integer  optional    

Token lifetime, 60-86400. Defaults to 900. Example: 900

Endpoints

POST api/v1/admin/organizations

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/organizations" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"owner_email\": \"zbailey@example.net\",
    \"owner_password\": \"i\",
    \"owner_name\": \"y\",
    \"abilities\": [
        \"write\"
    ]
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/organizations"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "owner_email": "zbailey@example.net",
    "owner_password": "i",
    "owner_name": "y",
    "abilities": [
        "write"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/organizations

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Must be at least 2 characters. Must not be greater than 120 characters. Example: b

owner_email   string     

Must be a valid email address. Must not be greater than 255 characters. Example: zbailey@example.net

owner_password   string     

Must be at least 12 characters. Must not be greater than 200 characters. Example: i

owner_name   string     

Must be at least 2 characters. Must not be greater than 120 characters. Example: y

abilities   string[]  optional    
Must be one of:
  • read
  • write
  • send
  • admin

POST api/v1/admin/embed-secret/sync

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/embed-secret/sync" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/embed-secret/sync"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/v1/admin/embed-secret/sync

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

POST api/v1/admin/embed-secret/provision

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/embed-secret/provision" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"label\": \"b\",
    \"issuer\": \"n\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/embed-secret/provision"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "label": "b",
    "issuer": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/embed-secret/provision

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

label   string     

Must not be greater than 120 characters. Example: b

issuer   string     

Must match the regex /^[A-Za-z0-9._-]+$/. Must not be greater than 120 characters. Example: n

POST api/webhooks/telegram/{ulid}

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/webhooks/telegram/architecto" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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());

Request      

POST api/webhooks/telegram/{ulid}

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

POST api/webhooks/instagram

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/webhooks/instagram" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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());

Request      

POST api/webhooks/instagram

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

POST api/webhooks/twitter

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/webhooks/twitter" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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());

Request      

POST api/webhooks/twitter

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

POST api/integrations/meta/embedded-signup

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

POST api/integrations/meta/embedded-signup

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string     

Must not be greater than 1024 characters. Example: b

waba_id   string     

Must not be greater than 64 characters. Example: n

POST api/integrations/qr/sessions

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

POST api/integrations/qr/sessions

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

display_name   string     

Must be at least 2 characters. Must not be greater than 80 characters. Example: b

POST api/v1/admin/messages/transactional

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/messages/transactional" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"to\": \"+3642559314\",
    \"channel_id\": \"architecto\",
    \"type\": \"template\",
    \"text\": \"n\",
    \"template\": {
        \"name\": \"g\",
        \"language\": \"zmiyvdljnikhwayk\",
        \"components\": [
            {
                \"type\": \"architecto\"
            }
        ]
    }
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/messages/transactional"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "to": "+3642559314",
    "channel_id": "architecto",
    "type": "template",
    "text": "n",
    "template": {
        "name": "g",
        "language": "zmiyvdljnikhwayk",
        "components": [
            {
                "type": "architecto"
            }
        ]
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/messages/transactional

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

to   string     

Must match the regex /^+[1-9]\d{6,14}$/. Example: +3642559314

channel_id   string  optional    

Example: architecto

type   string     

Example: template

Must be one of:
  • template
  • text
text   string  optional    

This field is required when type is text. Must not be greater than 4096 characters. Example: n

template   object  optional    

This field is required when type is template.

name   string  optional    

This field is required when type is template. Must not be greater than 120 characters. Example: g

language   string  optional    

This field is required when type is template. Must not be greater than 16 characters. Example: zmiyvdljnikhwayk

components   object[]  optional    
type   string  optional    

This field is required when template.components is present. Example: architecto

parameters   object  optional    
metadata   object  optional    

POST api/v1/admin/messages/otp

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/messages/otp" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"to\": \"+3642559314\",
    \"code\": \"56425\",
    \"ttl_seconds\": 61,
    \"purpose\": \"password_reset\",
    \"locale\": \"en\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/messages/otp"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "to": "+3642559314",
    "code": "56425",
    "ttl_seconds": 61,
    "purpose": "password_reset",
    "locale": "en"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/messages/otp

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

to   string     

Must match the regex /^+[1-9]\d{6,14}$/. Example: +3642559314

code   string     

Must match the regex /^\d{4,8}$/. Example: 56425

ttl_seconds   integer     

Must be between 60 and 3600. Example: 61

purpose   string     

Example: password_reset

Must be one of:
  • registration
  • two_factor
  • password_reset
  • email_verify
  • other
locale   string     

Example: en

Must be one of:
  • ar
  • en
metadata   object  optional    

POST api/webhooks/neoleap

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/webhooks/neoleap" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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());

Request      

POST api/webhooks/neoleap

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Generate and store a new Embed SSO shared secret, returning the plain-text value so the caller can persist it on their side.

requires authentication

Rotating replaces the previous secret immediately; any outstanding tokens signed with the old secret will fail verification.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/embed-sso/provision" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/embed-sso/provision"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/v1/admin/embed-sso/provision

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

POST api/v1/admin/conversations/{ulid}/messages

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/messages" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"image\",
    \"body\": \"b\",
    \"caption\": \"n\",
    \"media_url\": \"http:\\/\\/crooks.biz\\/et-fugiat-sunt-nihil-accusantium\",
    \"template_name\": \"n\",
    \"language\": \"ikhwaykcmyuwpwlv\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "image",
    "body": "b",
    "caption": "n",
    "media_url": "http:\/\/crooks.biz\/et-fugiat-sunt-nihil-accusantium",
    "template_name": "n",
    "language": "ikhwaykcmyuwpwlv"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/conversations/{ulid}/messages

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

type   string     

Example: image

Must be one of:
  • text
  • image
  • document
  • audio
  • video
  • template
body   string  optional    

This field is required when type is text. Must not be greater than 4096 characters. Example: b

caption   string  optional    

Must not be greater than 1024 characters. Example: n

media_url   string  optional    

This field is required when type is image, document, audio, or video. Must be a valid URL. Must not be greater than 2048 characters. Example: http://crooks.biz/et-fugiat-sunt-nihil-accusantium

template_name   string  optional    

This field is required when type is template. Must not be greater than 120 characters. Example: n

language   string  optional    

This field is required when type is template. Must not be greater than 16 characters. Example: ikhwaykcmyuwpwlv

components   object  optional    

POST api/v1/admin/conversations/{ulid}/assign

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/assign" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_ulid\": \"bngzmiyvdljnikhwaykcmyuwpw\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/assign"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "user_ulid": "bngzmiyvdljnikhwaykcmyuwpw"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/conversations/{ulid}/assign

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

user_ulid   string  optional    

Must be 26 characters. Example: bngzmiyvdljnikhwaykcmyuwpw

POST api/v1/admin/conversations/{ulid}/read

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/read" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/conversations/architecto/read"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/v1/admin/conversations/{ulid}/read

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

POST api/v1/admin/contacts

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/admin/contacts" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"phone\": \"b\",
    \"name\": \"n\"
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/admin/contacts"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "phone": "b",
    "name": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/admin/contacts

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

phone   string     

Must be at least 5 characters. Must not be greater than 32 characters. Example: b

name   string  optional    

Must not be greater than 120 characters. Example: n

custom_fields   object  optional    

POST api/v1/groups

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

POST api/v1/groups

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

subject   string     

Must be at least 1 character. Must not be greater than 255 characters. Example: b

participants   string[]  optional    

Must be at least 6 characters. Must not be greater than 32 characters.

channel_id   integer  optional    

Example: 16

PATCH api/v1/groups/{ulid}

requires authentication

Example request:
curl --request PATCH \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

PATCH api/v1/groups/{ulid}

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

subject   string  optional    

Must be at least 1 character. Must not be greater than 255 characters. Example: b

description   string  optional    

Must not be greater than 2048 characters. Example: Et animi quos velit et fugiat.

POST api/v1/groups/{ulid}/participants

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

POST api/v1/groups/{ulid}/participants

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

participants   string[]  optional    

Must be at least 6 characters. Must not be greater than 32 characters.

DELETE api/v1/groups/{ulid}/participants

requires authentication

Example request:
curl --request DELETE \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

DELETE api/v1/groups/{ulid}/participants

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

participants   string[]  optional    

Must be at least 6 characters. Must not be greater than 96 characters.

PUT api/v1/groups/{ulid}/picture

requires authentication

Example request:
curl --request PUT \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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());

Request      

PUT api/v1/groups/{ulid}/picture

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

Body Parameters

picture_base64   string     

Must be at least 64 characters. Example: bngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtnnoqi

POST api/v1/groups/{ulid}/sync

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/groups/architecto/sync" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/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());

Request      

POST api/v1/groups/{ulid}/sync

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

Example: architecto

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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": [
            "..."
        ]
    }
}
 

Request      

POST api/v1/messages

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

channel_id   string  optional    

The channel ULID. Required when sending by wa_id. Example: 01HZK7P5R3Q6V0YH4XJ3M8CHN1

conversation_id   string  optional    

Reuse an existing conversation by ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8CONV1

wa_id   string  optional    

Destination wa_id (E.164 without +). Required unless conversation_id is set. Example: 966500000000

type   string  optional    

The message type: text|image|document|audio|video. Defaults to "text". Example: text

body   string     

The message text or media caption. Example: Your order is on the way!

media_url   string  optional    

For image/document/audio/video — a publicly fetchable HTTPS URL. Example: https://cdn.example.com/file.pdf

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:

Exchange code for token

requires authentication

Example request:
curl --request POST \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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."
}
 

Request      

POST api/v1/oauth/token

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string     

The one-time code from the /connect redirect. Example: 4f9e2c...

redirect_uri   string     

Must match the redirect_uri the user authorized on the consent screen. Example: https://crm.example.com/oktawa/callback

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.

Example request:
curl --request GET \
    --get "https://connect.getokta.io/api/v1/social-posts?status=scheduled&per_page=25" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/social-posts"
);

const params = {
    "status": "scheduled",
    "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": "01HZK7P5R3Q6V0YH4XJ3M8PST1",
            "status": "scheduled",
            "body": "New drop is live!",
            "media": [],
            "options": null,
            "scheduled_at": "2026-07-05T09:00:00+00:00",
            "published_at": null,
            "target_count": 1,
            "published_count": 0,
            "failed_count": 0,
            "created_at": "2026-07-04T08:00:00+00:00",
            "targets": [
                {
                    "channel_id": "01HZK7P5R3Q6V0YH4XJ3M8CHN1",
                    "status": "pending",
                    "target_ref": null,
                    "permalink": null,
                    "provider_post_id": null,
                    "published_at": null
                }
            ]
        }
    ],
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1
    }
}
 

Request      

GET api/v1/social-posts

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status (draft, scheduled, publishing, published, partially_failed, failed, cancelled). Example: scheduled

per_page   integer  optional    

Page size, 1-100. Defaults to 25. Example: 25

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.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/social-posts" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"text\": \"New drop is live!\",
    \"channel_ids\": [
        \"01HZK7P5R3Q6V0YH4XJ3M8CHN1\"
    ],
    \"scheduled_at\": \"2026-07-05T09:00:00+00:00\",
    \"media\": [
        {
            \"url\": \"https:\\/\\/cdn.example.com\\/promo.jpg\",
            \"type\": \"image\"
        }
    ]
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/social-posts"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "text": "New drop is live!",
    "channel_ids": [
        "01HZK7P5R3Q6V0YH4XJ3M8CHN1"
    ],
    "scheduled_at": "2026-07-05T09:00:00+00:00",
    "media": [
        {
            "url": "https:\/\/cdn.example.com\/promo.jpg",
            "type": "image"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "data": {
        "id": "01HZK7P5R3Q6V0YH4XJ3M8PST1",
        "status": "scheduled",
        "body": "New drop is live!",
        "target_count": 1,
        "scheduled_at": "2026-07-05T09:00:00+00:00"
    }
}
 

Example response (422):


{
    "message": "One or more channels were not found.",
    "errors": {
        "channel_ids": [
            "One or more channels were not found."
        ]
    }
}
 

Request      

POST api/v1/social-posts

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

text   string     

The post caption/body. Example: New drop is live!

channel_ids   string[]     

Channel ULIDs to publish to.

scheduled_at   string  optional    

ISO-8601 publish time; omit for a draft. Example: 2026-07-05T09:00:00+00:00

media   object[]  optional    

Media items to attach.

url   string  optional    

A publicly fetchable HTTPS URL. Example: https://cdn.example.com/promo.jpg

type   string  optional    

Media type hint (image, video). Example: image

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.

Example request:
curl --request GET \
    --get "https://connect.getokta.io/api/v1/tags?scope=contact&per_page=50" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://connect.getokta.io/api/v1/tags"
);

const params = {
    "scope": "contact",
    "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": "01HZK7P5R3Q6V0YH4XJ3M8TAG1",
            "name": "VIP",
            "slug": "vip",
            "color": "#f59e0b",
            "scope": "contact",
            "created_at": "2026-07-04T08:00:00+00:00"
        }
    ],
    "meta": {
        "current_page": 1,
        "per_page": 50,
        "total": 1
    }
}
 

Request      

GET api/v1/tags

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

scope   string  optional    

Filter by scope (contact, conversation). Example: contact

per_page   integer  optional    

Page size, 1-100. Defaults to 50. Example: 50

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.

Example request:
curl --request POST \
    "https://connect.getokta.io/api/v1/contacts/01HZK7P5R3Q6V0YH4XJ3M8AAA1/tags" \
    --header "Authorization: Bearer {YOUR_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"tags\": [
        \"vip\",
        \"riyadh\"
    ]
}"
const url = new URL(
    "https://connect.getokta.io/api/v1/contacts/01HZK7P5R3Q6V0YH4XJ3M8AAA1/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_API_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tags": [
        "vip",
        "riyadh"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "data": {
        "id": "01HZK7P5R3Q6V0YH4XJ3M8AAA1",
        "wa_id": "966500000000",
        "name": "Ali",
        "tags": [
            "vip",
            "riyadh"
        ]
    }
}
 

Example response (404):


{
    "message": "No query results for model [Contact]."
}
 

Example response (422):


{
    "message": "The tags field is required.",
    "errors": {
        "tags": [
            "The tags field is required."
        ]
    }
}
 

Request      

POST api/v1/contacts/{id}/tags

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The contact ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8AAA1

Body Parameters

tags   string[]     

Tag names/slugs to apply.

Templates

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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."
}
 

Request      

POST api/v1/templates/send

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

channel_id   string     

Channel ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8CHN1

wa_id   string     

Destination wa_id. Example: 966500000000

template_name   string     

Template name as registered with Meta. Example: order_ready

language   string  optional    

Template language code. Defaults to the template's primary language. Example: ar

variables   string[]  optional    

Positional variable values for the body.

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 "https://connect.getokta.io/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(
    "https://connect.getokta.io/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
    }
}
 

Request      

GET api/v1/tickets

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by stage category (open, in_progress, on_hold, resolved, closed). Example: open

pipeline_id   string  optional    

Filter by pipeline ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8PIP1

per_page   integer  optional    

Page size, 1-100. Defaults to 25. Example: 25

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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."
        ]
    }
}
 

Request      

POST api/v1/tickets

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

subject   string     

Short summary of the issue. Example: Order not delivered

description   string  optional    

Longer description. Example: Customer says parcel never arrived.

pipeline_id   string  optional    

Pipeline ULID. Defaults to the org's default pipeline. Example: 01HZK7P5R3Q6V0YH4XJ3M8PIP1

contact_id   string  optional    

Related contact ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8AAA1

priority   string  optional    

Priority: low|normal|high|urgent. Defaults to "normal". Example: high

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 \
    "https://connect.getokta.io/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(
    "https://connect.getokta.io/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."
        ]
    }
}
 

Request      

POST api/v1/tickets/{ulid}/transition

Headers

Authorization        

Example: Bearer {YOUR_API_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ulid   string     

The ticket ULID. Example: 01HZK7P5R3Q6V0YH4XJ3M8TKT1

Body Parameters

stage_id   string     

Target stage ULID (must belong to the ticket's pipeline). Example: 01HZK7P5R3Q6V0YH4XJ3M8STG2

note   string  optional    

Optional note describing the transition. Example: Escalated to logistics.