Zaapi Zaapi Developer
Open Zaapi

Zaapi Open API Platform (1.0.0)

Read and manage your Zaapi store data programmatically.

The Zaapi Open API Platform lets you access your store's data directly from your own systems, without going through the Zaapi App. To be notified when something changes, subscribe to webhooks.

Getting started

1. Generate an API key

API keys are created and managed by you, inside the Zaapi App. There is no separate application or approval step, and no client secret to exchange.

  1. Open the Zaapi App and go to Settings → Developers.
  2. Select Add API key.
  3. Give the key a name that describes where it will be used, for example Warehouse sync or Reporting dashboard.
  4. Tick the scopes the key should have — at least one is required. A key can only call endpoints covered by the scopes you grant it — see Scopes below. Ticking a write scope also grants the matching read scope.
  5. Select Create, then copy the key.

The key is shown once, at creation. Store it somewhere safe before closing the dialog — you will not be able to see it again. Keys start with zaapi_. The table on the Developers page only shows a masked value (zaapi_•••••••• plus the last four characters).

If you lose the secret you don't have to start over. Use Rotate key on that row: it issues a new secret, keeps the name and scopes, and restarts the 6-month expiry.

Keys expire 6 months after they are created or last rotated. The expiry date is on the Developers page. An expired key is rejected with 401. Rotate before that date if the integration should keep working.

2. Send the key with every request

Pass the key in the x-api-key header. There is no bearer token, no Authorization header, and no token refresh — the key itself is the credential. A signed request, and the 401 / 403 bodies you get back when the header is missing, unrecognised, or under-scoped, are shown in the samples panel.

Every request is authenticated and scope-checked before it reaches your store's data. A request with no x-api-key header, or an unrecognised, inactive, expired, or deleted key, is rejected with 401. A valid key missing the scope an endpoint requires is rejected with 403. On success the request is scoped to your store, so a caller can only ever see their own data.

3. Keep the key safe

A key carries the scopes you granted it and acts on your store, so treat it like a password.

  • Send it only from a server you control. Never embed a key in a mobile app, a browser page, or any client-side code, where it can be read by anyone.
  • Use a separate key per integration. If one has to be deleted, the others keep working.
  • Grant only the scopes an integration actually needs. A reporting tool that only reads data should not hold write scopes. The form has a Read only shortcut that selects every read scope and none of the write scopes.
  • Rotate a key if it may have been exposed, if you lost the secret, or before it expires. Rotating is the usual response to a leaked or lost key, and it restarts the 6-month expiry.
  • Delete a key when the integration using it is being retired.

4. Edit, rotate, or delete a key

Each of these lives on the key's ⋮ menu on Settings → Developers.

Editing renames the key or changes its scopes. The secret itself does not change, and nothing is shown again — integrations already using the key keep working. Taking a scope away starts failing those calls with 403 on the next request.

Rotating issues a new secret for the same key and leaves the name and scopes untouched. The new secret is shown once, the same as at creation, and the 6-month expiry restarts today.

The old secret stays valid for two hours. Use that window to deploy the new one. After two hours, anything still sending the old secret fails with 401. If your integration reads the key from a secrets store it can re-read without restarting, update it there and the cutover is immediate.

If two hours is not enough, add a second key with the same scopes, move the integration onto it, confirm it's working, then delete the first.

Rotate when a key has been lost, when it may have been exposed, or before it expires.

Deleting removes a key permanently. It takes effect immediately — the next request using that key fails with 401 — and it cannot be undone. Delete when the integration using a key is being retired for good.

Who can do this. Store owners and admins can add, edit, rotate, and delete API keys. The Developers page is not shown to other roles.

5. Handle the common errors

Failed requests return the same envelope. 401 and 403 from a missing, unrecognised, or under-scoped x-api-key header are shown in the samples panel. Application errors (400, 404, and endpoint-specific failures) look like:

{
  "requestId": "<requestId>",
  "requestReceivedAt": "1757580000000",
  "storeId": "<id>",
  "error": {
    "message": "Resource not found",
    "statusCode": 404
  }
}

Four failures can happen on any request, so they are described here once rather than repeated on every endpoint below.

400 — the request was malformed or failed validation. Check the request body and query parameters against the endpoint's schema. error.message names the offending field.

401 — the x-api-key header is missing, or the key is not recognised, inactive, expired, or has been deleted. Check that the header is being sent and that the key is correct. If this started more than two hours after a rotation, the old secret is still deployed somewhere. If the key was lost, rotate it. If it expired, rotate it before the next call. If it was deleted, add a new key.

403 — the x-api-key header is valid but was not granted the scope this endpoint requires. Check the scope listed on the endpoint. Edit the key on Settings → Developers to add that scope, or use a different key that already has it.

404 — no such record in your store, or the key cannot reach it. Check the ID. A record belonging to another store returns 404, not 403.

409 — the request conflicts with current store data. Returned when updating a ticket or contact field with an out-of-date resource timestamp, or when creating a contact whose email or phone is already on the store. Re-read the resource and retry with its latest field timestamp, or use a different email or phone.

Treat 401 and 403 as permanent. Retrying with the same key produces the same result, and a retry loop against 401 will simply generate noise. Both need a change to the key itself.

Endpoints that can fail in ways specific to what they do — a conflicting write, for example — document those responses individually.

Scopes

Scopes decide what a key is allowed to do. You choose them when the key is created, and you can change them later with Edit on that key. Rotating a key preserves its scopes. Granting a write scope also grants the matching read scope; removing a read scope removes the matching write scope.

Each resource has a read and write pair. Write scopes for accounts, users, labels, and teams are defined so keys issued today do not need reissuing when those routes land.

  • tickets:read — read tickets and ticket field definitions
  • tickets:write — assign tickets, update ticket fields, and close tickets
  • conversations:read — read conversations
  • conversations:write — mark conversations read and update labels
  • contacts:read — read and search contacts, and read contact field definitions
  • contacts:write — create contacts and update contact fields
  • messages:read — read, search messages, and read internal comments
  • messages:write — send messages, including email and WhatsApp templates, and create internal comments
  • accounts:read — read channel accounts and WhatsApp message templates
  • accounts:write — reserved for future write endpoints
  • users:read — read store users
  • users:write — reserved for future write endpoints
  • labels:read — read labels
  • labels:write — reserved for future write endpoints
  • teams:read — read teams
  • teams:write — reserved for future write endpoints

Each endpoint below states the scope it requires.

Conventions

URLs. Every path is relative to the versioned base URL, https://openplatform.zaapi.co/v1. Record IDs travel as query parameters on reads and in the request body on writes, rather than as path segments.

Rate limits. Each key is limited to:

  • Throttle — 20 requests per second
  • Burst — 40 requests per second
  • Quota — 100,000 requests per day

Response envelope. Successful responses wrap the result in data:

{
  "requestId": "<requestId>",
  "requestReceivedAt": "1757580000000",
  "storeId": "<id>",
  "data": { }
}

requestReceivedAt is Unix time in milliseconds, as a string. In JavaScript:

new Date(Number(body.requestReceivedAt))

A single-resource read puts an object in data. A list puts an array in data, including GET /conversations when you look up one conversation by id — that call still returns a one-item array. Failed requests use the same envelope with error instead of data.

Some writes return the updated resource. Others acknowledge the write with { "message": "OK" } inside data and do not echo the resource: marking a conversation read, updating conversation labels, updating a contact field, sending a plain-text message, sending an email, sending a WhatsApp template, and adding an internal comment.

Pagination

Collections that can grow without bound are cursor-paginated. Walk them with pageSize and cursor.

Query parameters on paginated endpoints:

  • pageSize — items per page. Optional. Defaults to 20, except GET /messages where it defaults to 15. Maximum is 100 on every endpoint that accepts pageSize. GET /messages/search and GET /contacts/search do not accept pageSize; each page contains 10 results. A value above 100, below 1, or that is not a whole number is rejected with 400.
  • cursor — opaque token from the previous page. Omit it on the first page. Pass the previous x-pagination-next-cursor value back verbatim — do not parse, wrap, or reconstruct it. A malformed cursor is rejected with 400.

The response body stays a JSON array inside data. Pagination metadata is returned as headers, not in the body:

  • x-pagination-page-size
  • x-pagination-next-cursor — present when another page may be available. A page can contain fewer than pageSize items and still include this header. When it is present, request the next page with the same filters and pageSize, plus cursor set to this value. Stop only when the header is missing.

First page:

GET /v1/labels?pageSize=20

Next page — same filters and pageSize, plus the cursor from x-pagination-next-cursor:

GET /v1/labels?pageSize=20&cursor=<cursor>

Keep pageSize and every filter the same across pages. Changing them mid-walk can reject the cursor, skip rows, or repeat rows.

These endpoints are cursor-paginated. All of them accept a pageSize of up to 100:

  • POST /tickets/list — newest message first
  • GET /conversations — most recent last message first
  • POST /contacts/list — most recently updated first without search; relevance first with search
  • GET /messages — newest first, default pageSize 15
  • GET /messages/internal-comments — newest first
  • GET /labels — most recently updated first
  • GET /teams — oldest first
  • GET /accounts — oldest first

GET /messages/search and GET /contacts/search are also cursor-paginated, in relevance order. Each page contains 10 results. Do not send pageSize. Pass cursor from x-pagination-next-cursor to continue.

Small per-store catalogs are returned in full, with no pagination parameters or x-pagination-* headers:

  • GET /tickets/fields — all ticket field definitions, display order
  • GET /contacts/fields — all contact field definitions, display order
  • GET /users — every user on the store

GET /accounts/whatsapp-templates returns at most 100 templates for one WhatsApp account. It does not take pageSize or cursor.

Pass expand as a query parameter to resolve related ids into small objects. Send a comma-separated list (expand=account,assignee) or repeat the key (expand=account&expand=assignee). Values are exact and case-sensitive. An unknown value is rejected with 400 (Invalid expand value: …).

Each endpoint accepts only the values listed on it. expand=assignee is valid on a ticket and rejected on a conversation — assignee lives on tickets, not conversations.

Two shapes are used:

Replace the id. On tickets and conversations, the id field is omitted and a related object takes its place.

GET /v1/tickets?ticketId=<id>
# { "accountId": "<id>", "assigneeUserId": "<id>" }

GET /v1/tickets?ticketId=<id>&expand=account,assignee
# { "account": { "id": "<id>", "name": "Line Shop", "channel": "line" },
#   "assignee": { "id": "<id>", "name": "Nadia Prasert" } }

Add a sibling, or replace a nested id. On messages, conversationId stays on the record and account / conversation / sentBy are added beside it. There is no accountId on the message. On an internal comment, expand=createdBy replaces noteData.createdBy in place. On a team, expand=users turns each member id into { id, name } when that user exists on the store — otherwise the raw id is left as-is. On a user, expand=role adds role { id, name } and omits roleId.

If a related record cannot be resolved, the expand is skipped for that field and the original id remains.

Allowed expand values:

Endpoint Values Effect
Ticket reads and writes account, conversation, assignee, handledBy Replaces accountId, conversationId, assigneeUserId, handledBy
GET /tickets/fields accounts Adds accounts (name, accountId, channel)
GET /conversations account Replaces accountId
GET /messages sentBy, conversation, account Adds those objects. conversationId stays
GET /messages/internal-comments createdBy, conversation, account Replaces noteData.createdBy; adds conversation and account
GET /users role Adds role; omits roleId
GET /teams users Each member becomes { id, name } when known

Related channel objects are { id, name, channel }. Related users are { id, name }, except message sentBy / createdBy, which are { userId, name, roleId }.

Timestamps. Resource timestamps are ISO 8601 in UTC. Envelope requestReceivedAt is Unix milliseconds as a string — see Response envelope above.

Errors. See Handle the common errors above for the shared error shape and the 400, 401, 403, 404, and 409 responses that apply across endpoints.

MCP

Install the @zaapi/open-api-mcp server so an assistant such as Claude can call this API on your store. Each operation becomes a tool. The server sends the API key you configure, and that key's scopes decide what the assistant can do.

The assistant starts the server with npx, which comes with Node.js. Install Node.js on the machine that runs the assistant.

1. Create a key for the assistant

  1. Open the Zaapi App and go to Settings → Developers.
  2. Select Add API key.
  3. Name the key for the assistant, for example Claude.
  4. Tick the scopes the assistant should have. A reporting assistant that only reads data should not hold write scopes.
  5. Select Create, then copy the key.

The key is shown once. Put it in the config below, on the machine that runs the assistant. The same rules in Keep the key safe apply.

2. Add the server in Claude Desktop

Claude Desktop runs the server on your machine.

  1. Open Claude Desktop.
  2. From the Claude menu in the menu bar, select Settings.
  3. Open Developer and select Edit Config.

That opens claude_desktop_config.json. Edit Config creates the file when it is missing.

  • macOS — ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows — %APPDATA%\Claude\claude_desktop_config.json

Add a zaapi entry. If mcpServers already lists other servers, add zaapi next to them.

{
  "mcpServers": {
    "zaapi": {
      "command": "npx",
      "args": ["-y", "@zaapi/open-api-mcp"],
      "env": {
        "ZAAPI_API_KEY": "zaapi_…"
      }
    }
  }
}

Save the file. Quit Claude from the menu, then open it again so it reloads the config. In a new chat, Zaapi is listed with the assistant's tools.

Variable Required What to set
ZAAPI_API_KEY Yes The key from Settings → Developers. The server sends it as x-api-key. Keys start with zaapi_.
ZAAPI_BASE_URL No Defaults to https://openplatform.zaapi.co. Set https://openplatform-staging.zaapi.co to call staging.

A missing, expired, or under-scoped key fails the tool call the same way a direct request fails. See Handle the common errors.

Other assistants that accept an mcpServers config use the JSON in Add the server in Claude Desktop.

Tickets

Individual pieces of work raised against a conversation — listing, reading, assigning, updating fields, and closing.

Get a ticket by ID

Required scope: tickets:read

Returns a single ticket. Pass expand=account,conversation,assignee,handledBy to replace those ids with names. Unknown expand values return 400.

query Parameters
ticketId
required
string
Example: ticketId=<id>
expand
string
Example: expand=account,assignee

Comma-separated related objects to include. Replaces the matching id field (accountId → account, conversationId → conversation, assigneeUserId → assignee, handledBy → handledByUser). Unknown values return 400.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Ticket)
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>

Request samples

curl "https://openplatform.zaapi.co/v1/tickets?ticketId=<id>" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "ticketNumber": "<ticketNumber>",
    • "accountId": "<id>",
    • "conversationId": "<id>",
    • "channel": "line",
    • "isOpen": true,
    • "openedAt": "<timestamp>",
    • "assigneeUserId": "<id>",
    • "isFollowUp": false,
    • "isSpam": false,
    • "ticketFields": {
      },
    • "ticketFieldsUpdatedAt": "<timestamp>",
    • "contactId": "<id>",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

List tickets

Required scope: tickets:read

Returns a page of tickets in your store, newest message first. With an empty body this is open, non-spam tickets that are not snoozed.

Paginate with pageSize (max 100) and cursor. Pass expand=account,conversation,assignee,handledBy to replace those ids with names.

Do not send assigneeUserIds together with unassigned: true.

query Parameters
pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

expand
string
Example: expand=account,assignee

Comma-separated related objects to include. Replaces the matching id field (accountId → account, conversationId → conversation, assigneeUserId → assignee, handledBy → handledByUser). Unknown values return 400.

Request Body schema: application/json
optional
statuses
Array of strings
Items Enum: "open" "closed"

Omit to return open tickets only. Send both values for all tickets.

conversationId
string

Restrict to tickets belonging to this conversation. Sent on its own, returns the conversation's full history — open, closed, and reopened tickets. Add statuses to narrow the result.

accountIds
Array of strings
assigneeUserIds
Array of strings
handledByUserIds
Array of strings
resolutionStatuses
Array of strings
Items Enum: "none" "human" "automation" "ai_full_resolution" "ai_partial_resolution"
unassigned
boolean

When true, only tickets with no assignee. Cannot be combined with assigneeUserIds.

isFollowUp
boolean
isSpam
boolean

Defaults to false (non-spam tickets).

object (AdvancedFilters)

Optional extra filters on labels, ticket fields, waiting time, and opened-at.

object (LabelFilter)
Array of objects (TicketFieldFilter)
object (WaitingForReplyFilter)

Tickets where the buyer has been waiting at least this many minutes.

object (OpenedAtFilter)

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Ticket)
Array
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>

Request samples

Content type
application/json
{
  • "statuses": [
    • "open"
    ],
  • "unassigned": true
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

List ticket fields

Required scope: tickets:read

Returns every custom ticket field definition for your store, in display order. This is a full catalog — it is not paginated. Pass expand=accounts to include the accounts each field applies to.

query Parameters
expand
string
Value: "accounts"
Example: expand=accounts

Pass accounts to include the accounts each field applies to.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (TicketField)
Array
id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 3 more
dataType
required
string
Enum: "string" "number" "boolean" "date"
sortOrder
required
integer

Display order among ticket fields.

isRequired
required
boolean
active
required
boolean
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
description
string
Array of objects (SelectOption)

Options for select fields.

regex
string

Validation pattern for free-text values.

Array of objects (ShowCondition)

When set, this field is only shown if these conditions match.

maxItems
integer

Maximum number of values when the field accepts more than one.

Array of objects (TicketFieldAccount)

Included when expand contains accounts.

Request samples

curl "https://openplatform.zaapi.co/v1/tickets/fields?expand=accounts" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Update a ticket field value

Required scope: tickets:write

Sets or clears one field on a ticket. Send null as newValue to unset it.

Send the ticketFieldsUpdatedAt value you last received for the ticket. If someone else has changed the fields since then, the request is rejected with 409 and nothing is written — re-read the ticket and try again. Omit ticketFieldsUpdatedAt when the ticket has never had a field update.

query Parameters
expand
string
Example: expand=account,assignee

Comma-separated related objects to include. Replaces the matching id field (accountId → account, conversationId → conversation, assigneeUserId → assignee, handledBy → handledByUser). Unknown values return 400.

Request Body schema: application/json
required
ticketId
required
string
ticketFieldId
required
string
newValue
required
any

Type depends on the field's dataType. Send null to unset.

ticketFieldsUpdatedAt
string <date-time>

The ticketFieldsUpdatedAt you currently hold. A mismatch results in 409. Omit if the ticket has never had a field update.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Ticket)
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>

Request samples

Content type
application/json
{
  • "ticketId": "<id>",
  • "ticketFieldId": "<id>",
  • "ticketFieldsUpdatedAt": "<timestamp>",
  • "newValue": "SO-10482"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "ticketNumber": "<ticketNumber>",
    • "accountId": "<id>",
    • "conversationId": "<id>",
    • "channel": "line",
    • "isOpen": true,
    • "openedAt": "<timestamp>",
    • "assigneeUserId": "<id>",
    • "isFollowUp": false,
    • "isSpam": false,
    • "ticketFields": {
      },
    • "ticketFieldsUpdatedAt": "<timestamp>",
    • "contactId": "<id>",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

Assign tickets

Required scope: tickets:write

Assigns between 1 and 50 tickets in one request. Each item names a ticket and the user to assign it to.

A ticket already assigned to that user is left unchanged and is still returned. Every ticket must be open and not spam, and the assignee must be a user on your store who can access that ticket's account. If any item fails those checks, nothing is assigned.

Send each ticket id once. The response lists the tickets in the same order you sent them. Pass expand=assignee to replace assigneeUserId with { id, name }.

query Parameters
expand
string
Example: expand=account,assignee

Comma-separated related objects to include. Replaces the matching id field (accountId → account, conversationId → conversation, assigneeUserId → assignee, handledBy → handledByUser). Unknown values return 400.

Request Body schema: application/json
required
Array ([ 1 .. 50 ] items)
ticketId
required
string

The ticket to assign.

assigneeUserId
required
string <uuid>

The user to assign the ticket to.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Ticket)
Array
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>

Request samples

Content type
application/json
[
  • {
    • "ticketId": "<id>",
    • "assigneeUserId": "<id>"
    }
]

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Close a ticket

Required scope: tickets:write

Closes a ticket. You can record who handled it, conversion details, and ticket field values in the same request.

query Parameters
expand
string
Example: expand=account,assignee

Comma-separated related objects to include. Replaces the matching id field (accountId → account, conversationId → conversation, assigneeUserId → assignee, handledBy → handledByUser). Unknown values return 400.

Request Body schema: application/json
required
ticketId
required
string
handledBy
string <uuid>

The user who handled the ticket.

isConverted
boolean

Whether the conversation led to a conversion.

totalRevenue
number >= 0

Conversion revenue recorded on close.

currency
string

ISO 4217 code for totalRevenue.

object

Field values to set when closing, keyed by field id.

property name*
additional property
any

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Ticket)
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>

Request samples

Content type
application/json
{
  • "ticketId": "<id>",
  • "handledBy": "<id>",
  • "isConverted": true,
  • "totalRevenue": 1890,
  • "currency": "THB"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "ticketNumber": "<ticketNumber>",
    • "accountId": "<id>",
    • "conversationId": "<id>",
    • "channel": "line",
    • "isOpen": true,
    • "openedAt": "<timestamp>",
    • "assigneeUserId": "<id>",
    • "isFollowUp": false,
    • "isSpam": false,
    • "ticketFields": {
      },
    • "ticketFieldsUpdatedAt": "<timestamp>",
    • "contactId": "<id>",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

Conversations

Channel conversations — finding by conversation or contact, marking read, and applying label changes.

Filter conversations by ID or contact

Required scope: conversations:read

Returns a page of conversations. Send conversationId and/or contactId — at least one is required. A conversationId lookup returns a one-item page, or 404 if that conversation is not in your store. contactId pages through that contact's conversations, most recent last message first.

If both are sent they must refer to the same conversation, otherwise the response is 404.

Paginate with pageSize (max 100) and cursor. Pass expand=account to replace accountId with { id, name, channel }. Assignee is not available on conversations — it lives on tickets.

query Parameters
conversationId
string
Example: conversationId=<id>
contactId
string
Example: contactId=<id>
pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

expand
string
Value: "account"
Example: expand=account

Pass account to replace accountId with { id, name, channel }. assignee is not a valid expand on conversations.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Conversation)
Array
id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
displayName
required
string
contactId
required
string
referenceId
required
string

The channel's own conversation id.

labelIds
required
Array of strings
isSpam
required
boolean
unreadMsgCnt
required
integer
allMsgCnt
required
integer
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

emailSubject
string

Present on email channels only.

lastRecipientMsgDate
string <date-time>
object

Request samples

curl "https://openplatform.zaapi.co/v1/conversations?conversationId=<id>&expand=account" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Mark a conversation as read

Required scope: conversations:write

Clears the unread count on the conversation's open ticket. Idempotent — calling it on an already-read conversation is a no-op. A conversation with no open ticket returns 404.

Request Body schema: application/json
required
conversationId
required
string

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "conversationId": "<id>"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Update conversation labels

Required scope: conversations:write

Applies a label delta. At least one of addLabels or removeLabels is required. Each array accepts at most 10 ids, and every id must identify a label in your store. An unknown label id returns 400 and leaves the conversation unchanged. Adding a label already present, or removing one that is absent, is a no-op.

Request Body schema: application/json
required
Any of
addLabels
required
Array of strings [ 1 .. 10 ] items
conversationId
required
string
removeLabels
Array of strings <= 10 items

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "removeLabels": [
    • "<id>"
    ],
  • "conversationId": "<id>",
  • "addLabels": [
    • "<id>"
    ]
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Contacts

Customer contacts — reading, searching, listing, creating, and updating custom fields.

Get a contact by ID

Required scope: contacts:read

Returns a single contact. Use GET /contacts/search to search by text, or POST /contacts/list for a page of full contacts.

query Parameters
contactId
required
string
Example: contactId=<id>

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Contact)
id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

Request samples

curl "https://openplatform.zaapi.co/v1/contacts?contactId=<id>" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "fullName": "Somchai Srisuk",
    • "firstName": "Somchai",
    • "lastName": "Srisuk",
    • "email": "[email protected]",
    • "phone": "+66812345678",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

Create a contact

Required scope: contacts:write

Creates a contact in your store. Rejected if another contact already has the same primary email or phone. Phone must be E.164.

Request Body schema: application/json
required
firstName
required
string <= 50 characters
lastName
string <= 50 characters
email
string <email> <= 100 characters
phone
string <= 15 characters

Primary phone number in E.164 format.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Contact)
id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

Request samples

Content type
application/json
{
  • "firstName": "Somchai",
  • "lastName": "Srisuk",
  • "email": "[email protected]",
  • "phone": "+66812345678"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "fullName": "Somchai Srisuk",
    • "firstName": "Somchai",
    • "lastName": "Srisuk",
    • "email": "[email protected]",
    • "phone": "+66812345678",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

Search contacts

Required scope: contacts:read

Searches contacts in your store. Each page contains 10 contacts, most relevant first. Pass cursor from the previous x-pagination-next-cursor header to continue. Do not send pageSize.

query is required, from 1 to 150 characters. Each hit includes the fields that matched in matchedFields. This is not the full contact — use GET /contacts with contactId for the complete record.

query Parameters
query
required
string [ 1 .. 150 ] characters
Example: query=Somchai
cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (ContactSearchHit)
Array
id
required
string
fullName
required
string
matchedFields
required
Array of strings

Fields that matched the query.

phone
string
email
string <email>
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string

Request samples

curl "https://openplatform.zaapi.co/v1/contacts/search?query=Somchai" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

List contacts

Required scope: contacts:read

Returns contacts matching the optional search term and updated-at window. Without search, results are ordered by most recently updated. With search, results are ordered by relevance, then by most recently updated. Whitespace-only search is treated as omitted. Paginate with pageSize (max 100) and cursor.

query Parameters
pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

Request Body schema: application/json
optional
search
string <= 100 characters

Free-text search across name, email, and phone.

updatedAfter
string <date-time>

Only contacts updated on or after this time.

updatedBefore
string <date-time>

Only contacts updated before this time.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Contact)
Array
id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

Request samples

Content type
application/json
{
  • "search": "somchai"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

List contact fields

Required scope: contacts:read

Returns every custom contact field definition for your store, in display order. This is a full catalog — it is not paginated. Store limits: 100 fields total, 20 active, and 10 values on a multi-select. selectOptions is a nested tree with a maximum depth of 4.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (ContactField)
Array
id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 1 more
dataType
required
string
Enum: "number" "boolean" "string" "date"
active
required
boolean
sortOrder
required
integer

Display order among contact fields.

description
string
Array of objects (SelectOption)

Nested options for select fields. Maximum depth 4.

regex
string

Validation pattern for free-text values.

maxItems
integer

Maximum number of values when the field accepts more than one.

Request samples

curl "https://openplatform.zaapi.co/v1/contacts/fields" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Update a contact field value

Required scope: contacts:write

Sets or clears one custom field on a contact. Send null as value to unset it. Pass the customFieldsUpdatedAt you last read as updatedAt. If customFieldsUpdatedAt is absent, pass the contact's updatedAt instead. If the contact changed in the meantime the write is rejected with 409. Re-read the contact and retry with its latest timestamp. Values are validated against the field definition (regex, max items, select options).

Request Body schema: application/json
required
contactId
required
string
contactFieldId
required
string
value
required
any

Type depends on the field's dataType. Send null to unset.

updatedAt
required
string <date-time>

The customFieldsUpdatedAt you currently hold, or the contact's updatedAt if customFieldsUpdatedAt is absent. An out-of-date value is rejected with 409.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "contactId": "<id>",
  • "contactFieldId": "<id>",
  • "updatedAt": "<timestamp>",
  • "value": "VIP"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Messages

Customer messages and internal comments, including email and WhatsApp template sends.

List messages

Required scope: messages:read

Returns customer-facing messages, newest first. Events and internal comments are excluded. Send conversationId and/or ticketId — at least one is required. When ticketId is set, results are limited to that ticket's time window (after the previous ticket on the conversation closed, through this ticket's closedAt if it is closed). If both ids are sent and do not belong together, the response is 404.

Each message includes id, conversationId, messageDirection, and sentAt. text is the payload text, when there is any. images lists image URLs, when the message has any. messageDirection is inbound when the contact sent it and outbound when the store sent it. messageCategory is message. type, when set, is how the message was produced: auto_response, human_agent, one_time_noti, seller_only_messages, or deferred. sender is channel sender metadata, when present.

Paginate with pageSize (defaults to 15 here, max 100) and cursor. Pass expand=sentBy,conversation,account to add related names. conversationId stays on the message.

query Parameters
conversationId
string
Example: conversationId=<id>
ticketId
string
Example: ticketId=<id>

Limit results to this ticket's time window.

pageSize
integer [ 1 .. 100 ]
Default: 15
Example: pageSize=15

Items per page. Defaults to 15 on this endpoint. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

expand
string
Example: expand=sentBy,account

Comma-separated related objects to add beside the message ids. sentBy is { userId, name, roleId }. conversation and account are { id, name, channel }.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Message)
Array
id
required
string
conversationId
required
string
messageDirection
required
string
Enum: "inbound" "outbound"

inbound when the contact sent the message, outbound when the store sent it.

messageCategory
required
string
Value: "message"

Customer-facing messages are message.

sentAt
required
string <date-time>
text
string

Payload text, when the message has any.

images
Array of strings <uri> [ items <uri > ]

Image URLs, when the message has any.

type
string
Enum: "auto_response" "human_agent" "one_time_noti" "seller_only_messages" "deferred"

How the message was produced, when set.

object

Channel sender metadata, when present.

object

Present when expand includes conversation.

object

Present when expand includes account.

object

Present when expand includes sentBy.

Request samples

curl "https://openplatform.zaapi.co/v1/messages?conversationId=<id>&pageSize=15&expand=sentBy,account" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Search messages

Required scope: messages:read

Searches message text. Each page contains 10 messages, most relevant first. Pass cursor from the previous x-pagination-next-cursor header to continue. Do not send pageSize.

query is required, from 1 to 150 characters. sentByRecipient is true when the contact sent the message. score is the relevance score for that hit.

query Parameters
query
required
string [ 1 .. 150 ] characters
Example: query=cancel my order
cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (MessageSearchHit)
Array
id
required
string
conversationId
required
string
text
required
string
sentByRecipient
required
boolean

true when the contact sent the message.

sentAt
required
string <date-time>
score
required
number

Relevance score for this hit.

Request samples

curl "https://openplatform.zaapi.co/v1/messages/search?query=cancel%20my%20order" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

List internal comments

Required scope: messages:read

Returns seller-private notes written by agents about a customer on a conversation, newest first. These do not reach the customer. Paginate with pageSize (max 100) and cursor. Pass expand=createdBy,conversation,account to resolve related names.

query Parameters
conversationId
required
string
Example: conversationId=<id>
pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

expand
string
Example: expand=createdBy,conversation,account

Comma-separated related objects. createdBy replaces noteData.createdBy in place. conversation and account are added beside their ids.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (InternalComment)
Array
id
required
string
conversationId
required
string
accountId
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
object

Present when expand includes conversation.

object

Present when expand includes account.

authorUserId
string
object
sentAt
string <date-time>

Request samples

curl "https://openplatform.zaapi.co/v1/messages/internal-comments?conversationId=<id>&expand=createdBy" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Add an internal comment

Required scope: messages:write

Saves an internal note on a conversation. Does not reach the customer. Plain text only, not HTML.

Tag a user by embedding <@userId:name@> in text. userId is that user's userId from GET /users, and name is their display name. Repeat the token to tag more than one person. Example: Can you take this, <@3f2a9c1e-8b4d-4e71-9a06-2c8f1b7d4e90:John Smith@>?

Request Body schema: application/json
required
conversationId
required
string
text
required
string non-empty

Comment body. Tag a user with <@userId:name@> — userId from GET /users, and their display name.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "conversationId": "<id>",
  • "text": "Can you take this, <@3f2a9c1e-8b4d-4e71-9a06-2c8f1b7d4e90:John Smith@>?"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Send a plain text message

Required scope: messages:write

Sends a plain text message to the customer on their channel. Channel rules apply — for example WhatsApp outside the 24-hour window requires an approved template. Retries send a second message; there is no idempotency key yet.

Request Body schema: application/json
required
conversationId
required
string
text
required
string [ 1 .. 1000 ] characters

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "conversationId": "<id>",
  • "text": "Thanks for your order!"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Start an email thread

Required scope: messages:write

Sends an email from a Gmail or Outlook account and starts a conversation. accountId is an id from GET /accounts whose channel is gmail or outlook.

Send text, html, or both. At least one must be a non-empty string. to is required and accepts 1–50 addresses. cc, bcc, and subject are optional.

The response acknowledges the send and does not include the new conversation. A retry sends another email.

Request Body schema: application/json
required
accountId
required
string

Gmail or Outlook account to send from. Use an id from GET /accounts.

required
Array of objects (EmailAddress) [ 1 .. 50 ] items

Primary recipients.

Array ([ 1 .. 50 ] items)
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Blind carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

subject
string

Subject line.

text
string

Plain text body.

html
string

HTML body.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "accountId": "<id>",
  • "to": [],
  • "subject": "Your order is on the way",
  • "text": "Your order is on the way."
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Send an email on a conversation

Required scope: messages:write

Sends an email on an existing Gmail or Outlook conversation. Send text, html, or both. At least one must be a non-empty string. to is required, because Outlook cannot infer recipients from the thread.

The response acknowledges the send. A retry sends another email.

Request Body schema: application/json
required
conversationId
required
string

Gmail or Outlook conversation to send on.

required
Array of objects (EmailAddress) [ 1 .. 50 ] items

Primary recipients.

Array ([ 1 .. 50 ] items)
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Blind carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

subject
string

Subject line.

text
string

Plain text body.

html
string

HTML body.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "conversationId": "<id>",
  • "to": [],
  • "subject": "Re: Your order",
  • "html": "<p>Your order is on the way.</p>"
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Send a WhatsApp template

Required scope: messages:write

Sends an approved WhatsApp template on an existing WhatsApp conversation. templateId comes from GET /accounts/whatsapp-templates.

When the template body contains {{1}}, {{2}}, and so on, pass variables in that order. Omit variables when the body has no placeholders.

The response acknowledges the send. A retry sends the template again.

Request Body schema: application/json
required
conversationId
required
string

WhatsApp conversation to send on.

templateId
required
string

Template id from GET /accounts/whatsapp-templates.

variables
Array of strings

Positional body values for a template whose body contains {{1}}, {{2}}, and so on, in that order.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "conversationId": "<id>",
  • "templateId": "<id>",
  • "variables": [
    • "Nadia",
    • "SO-10482"
    ]
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Send a WhatsApp template to a phone number

Required scope: messages:write

Sends an approved WhatsApp template to a phone number from a WhatsApp account. This starts a conversation when that phone has no thread on the account. accountId is an id from GET /accounts whose channel is whatsapp. phoneNumber is E.164, for example +66812345678.

templateId and variables work the same way as sending a template on an existing conversation.

The response acknowledges the send and does not include the conversation. A retry sends the template again.

Request Body schema: application/json
required
accountId
required
string

WhatsApp account to send from. Use an id from GET /accounts.

phoneNumber
required
string

Recipient phone number in E.164 format.

templateId
required
string

Template id from GET /accounts/whatsapp-templates.

variables
Array of strings

Positional body values for a template whose body contains {{1}}, {{2}}, and so on, in that order.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string

Request samples

Content type
application/json
{
  • "accountId": "<id>",
  • "phoneNumber": "+66812345678",
  • "templateId": "<id>",
  • "variables": [
    • "Nadia",
    • "SO-10482"
    ]
}

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

Users

Users who belong to the store.

List users

Required scope: users:read

Returns every user on the store. This is a full list — it is not paginated. Optionally filter to one userId (a UUID). An unknown userId returns an empty array. userId on a user may be absent for invited seats that have not yet been accepted. The display name may also be absent on an incomplete invitation.

Pass expand=role to include { id, name } for the user's role. Without expand, the response keeps roleId.

query Parameters
userId
string <uuid>
Example: userId=<id>

Restrict to this user (UUID).

expand
string
Value: "role"
Example: expand=role

Pass role to include { id, name } and omit roleId.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (User)
Array
id
required
string
name
string

Absent on incomplete invitations.

userId
string <uuid>

Absent for invited seats that have not yet been accepted.

roleId
string

Present unless expand includes role.

object

Replaces roleId when expand includes role.

Request samples

curl "https://openplatform.zaapi.co/v1/users?expand=role" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Labels

Labels that can be applied to conversations.

List labels

Required scope: labels:read

Returns labels for the store, most recently updated first. Paginate with pageSize (max 100) and cursor. Pass searchTerm (3–100 characters) to search the label text. Whitespace-only searchTerm is treated as omitted.

query Parameters
searchTerm
string [ 3 .. 100 ] characters
Example: searchTerm=vip

Optional case-insensitive search on the label text (3–100 characters).

pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Label)
Array
id
required
string
label
required
string
color
required
string
fbAdId
string
whatsAppAdId
string
metaPostId
string
whatsAppBroadcastId
string
isSystemLabel
boolean

Request samples

curl "https://openplatform.zaapi.co/v1/labels?pageSize=20" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Teams

Teams of store users.

List teams

Required scope: teams:read

Returns teams for the store, oldest first. Paginate with pageSize (max 100) and cursor. Pass userId (a UUID) to return only teams containing that user. Pass expand=users to return member { id, name } instead of raw user ids. A member with no record on the store stays a raw id.

query Parameters
userId
string <uuid>
Example: userId=<id>

Restrict to teams containing this user (UUID).

pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

expand
string
Value: "users"
Example: expand=users

Pass users to return each member as { id, name } when that user exists on the store. Otherwise the raw user id is left as-is.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Team)
Array
id
required
string
name
required
string
description
string
Array of strings or RelatedUser (object)

User ids by default. When expand includes users, each member becomes { id, name } if that user exists on the store, otherwise the raw id.

Request samples

curl "https://openplatform.zaapi.co/v1/teams?pageSize=20&expand=users" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Accounts

Channel accounts connected to the store, including WhatsApp message templates.

List accounts

Required scope: accounts:read

Returns a page of the channel accounts connected to your store. Only active accounts are returned. Pass channel to keep only the accounts on one channel. Use status to tell whether an account is currently healthy.

Paginate with pageSize (max 100) and cursor. Accounts are returned oldest first.

query Parameters
channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more

Return only the accounts on this channel.

pageSize
integer [ 1 .. 100 ]
Default: 20
Example: pageSize=20

Items per page. Defaults to 20. Maximum 100.

cursor
string
Example: cursor=<cursor>

Opaque token from the previous page's x-pagination-next-cursor header. Pass it back verbatim.

Responses

Response Headers
x-pagination-page-size
integer
Example: "20"

Page size used for this response.

x-pagination-next-cursor
string
Example: "<cursor>"

Pass as cursor to fetch the next page. A short page can still include this header. Stop only when the header is omitted.

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Account)
Array
id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
name
string

Display name of the account as shown on the channel.

status
string
Enum: "healthy" "unhealthy" "disconnected"

Absent on accounts that have never reported a status.

lastAuthorizedAt
string <date-time>

When the account was last authorised against the channel.

object

Channel metadata. Which fields are present depends on the channel.

Request samples

curl "https://openplatform.zaapi.co/v1/accounts?channel=line&pageSize=20" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

List WhatsApp message templates

Required scope: accounts:read

Returns message templates for one WhatsApp account, up to 100. accountId is an id from GET /accounts whose channel is whatsapp.

Filter with search (template name or body), language, status, or category. This list is not paginated. Narrow the filters when the account has more templates than the response includes.

Use id from a template as templateId when sending it. When a BODY component contains {{1}}, {{2}}, and so on, pass those values as variables, in that order.

query Parameters
accountId
required
string
Example: accountId=<id>

WhatsApp account to list templates for.

search
string
Example: search=order

Match template name or body text.

language
string
Example: language=en

Template language code.

status
string (WhatsappTemplateStatus)
Enum: "APPROVED" "ARCHIVED" "DELETED" "DISABLED" "FLAGGED" "IN_APPEAL" "LIMIT_EXCEEDED" "LOCKED" … 5 more

Template review status.

category
string (WhatsappTemplateCategory)
Enum: "MARKETING" "UTILITY"

Template category.

Responses

Response Schema: application/json
requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (WhatsappTemplate)
Array
id
required
string

Template id. Pass this as templateId when sending.

name
required
string
parameter_format
required
string

How body placeholders are filled. POSITIONAL uses {{1}}, {{2}}, in order.

language
required
string

Template language code.

status
required
string (WhatsappTemplateStatus)
Enum: "APPROVED" "ARCHIVED" "DELETED" "DISABLED" "FLAGGED" "IN_APPEAL" "LIMIT_EXCEEDED" "LOCKED" … 5 more
category
required
string (WhatsappTemplateCategory)
Enum: "MARKETING" "UTILITY"
required
Array of objects (WhatsappTemplateComponent)

Template parts. A BODY part may contain {{1}}, {{2}}, and so on. Pass those values as variables when you send the template.

object (WhatsappTemplateQualityScore)

Request samples

curl "https://openplatform.zaapi.co/v1/accounts/whatsapp-templates?accountId=<id>&status=APPROVED" \
  -H "x-api-key: YOUR_API_KEY"

Response samples

Content type
application/json
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Schemas

Error

requestId
required
string

Gateway request id. Include this when you contact support.

requestReceivedAt
required
string

Unix time in milliseconds, as a string ("1757580000000").

storeId
required
string
required
object
message
required
string

Human-readable description of what went wrong.

statusCode
required
integer

HTTP status code.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "error": {
    • "message": "Resource not found",
    • "statusCode": 404
    }
}

AcknowledgementResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object
message
required
string
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "message": "OK"
    }
}

TicketResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Ticket)
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "ticketNumber": "<ticketNumber>",
    • "accountId": "<id>",
    • "conversationId": "<id>",
    • "channel": "line",
    • "isOpen": true,
    • "openedAt": "<timestamp>",
    • "assigneeUserId": "<id>",
    • "isFollowUp": false,
    • "isSpam": false,
    • "ticketFields": {
      },
    • "ticketFieldsUpdatedAt": "<timestamp>",
    • "contactId": "<id>",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

TicketListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Ticket)
Array
id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

TicketFieldListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (TicketField)
Array
id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 3 more
dataType
required
string
Enum: "string" "number" "boolean" "date"
sortOrder
required
integer

Display order among ticket fields.

isRequired
required
boolean
active
required
boolean
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
description
string
Array of objects (SelectOption)

Options for select fields.

regex
string

Validation pattern for free-text values.

Array of objects (ShowCondition)

When set, this field is only shown if these conditions match.

maxItems
integer

Maximum number of values when the field accepts more than one.

Array of objects (TicketFieldAccount)

Included when expand contains accounts.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Ticket

id
required
string
ticketNumber
required
string

Human-readable ticket number.

isOpen
required
boolean
openedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
conversationId
string

Present unless expand includes conversation.

object

Replaces conversationId when expand includes conversation.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
channel
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
closedAt
string <date-time>
reopenedAt
string <date-time>
assigneeUserId
string

Present unless expand includes assignee.

object

Replaces assigneeUserId when expand includes assignee.

id
required
string
name
required
string
isFollowUp
boolean
isSpam
boolean
object

Custom field values keyed by field id, plus any of the system keys isConverted, salesRevenue, aiCsat, and aiSummary.

property name*
additional property
any
handledBy
string

Present unless expand includes handledBy.

object

Replaces handledBy when expand includes handledBy.

id
required
string
name
required
string
ticketFieldsUpdatedAt
string <date-time>

Send this back when updating a field so concurrent writes are rejected.

contactId
string
createdAt
string <date-time>
updatedAt
string <date-time>
{
  • "id": "<id>",
  • "ticketNumber": "<ticketNumber>",
  • "accountId": "<id>",
  • "conversationId": "<id>",
  • "channel": "line",
  • "isOpen": true,
  • "openedAt": "<timestamp>",
  • "assigneeUserId": "<id>",
  • "isFollowUp": false,
  • "isSpam": false,
  • "ticketFields": {
    • "<id>": "SO-10482",
    • "isConverted": true,
    • "salesRevenue": 1890,
    • "aiCsat": "5",
    • "aiSummary": "Customer asked about shipping"
    },
  • "ticketFieldsUpdatedAt": "<timestamp>",
  • "contactId": "<id>",
  • "createdAt": "<timestamp>",
  • "updatedAt": "<timestamp>"
}

RelatedChannel

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
{
  • "id": "string",
  • "name": "Line Shop",
  • "channel": "line"
}

RelatedUser

id
required
string
name
required
string
{
  • "id": "<id>",
  • "name": "Nadia Prasert"
}

TicketField

id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 3 more
dataType
required
string
Enum: "string" "number" "boolean" "date"
sortOrder
required
integer

Display order among ticket fields.

isRequired
required
boolean
active
required
boolean
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
description
string
Array of objects (SelectOption)

Options for select fields.

Array
required
string or number
children
Array of objects (SelectOption)

Nested options for cascading selects.

regex
string

Validation pattern for free-text values.

Array of objects (ShowCondition)

When set, this field is only shown if these conditions match.

Array
ticketFieldId
required
string
operator
required
string
Enum: "equals" "not_equals" "greater_than" "less_than" "greater_than_or_equals" "less_than_or_equals" "contains" "not_contains" … 2 more
value
required
any

Value to compare against. Type depends on the field.

maxItems
integer

Maximum number of values when the field accepts more than one.

Array of objects (TicketFieldAccount)

Included when expand contains accounts.

Array
name
required
string
accountId
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
{
  • "id": "<id>",
  • "name": "Order number",
  • "description": "Customer order reference",
  • "displayType": "text",
  • "dataType": "string",
  • "sortOrder": 0,
  • "isRequired": false,
  • "active": true,
  • "createdAt": "<timestamp>",
  • "updatedAt": "<timestamp>"
}

SelectOption

required
string or number
One of
string
children
Array of objects (SelectOption)

Nested options for cascading selects.

{
  • "value": "apple",
  • "children": [
    • { }
    ]
}

ShowCondition

ticketFieldId
required
string
operator
required
string
Enum: "equals" "not_equals" "greater_than" "less_than" "greater_than_or_equals" "less_than_or_equals" "contains" "not_contains" … 2 more
value
required
any

Value to compare against. Type depends on the field.

{
  • "ticketFieldId": "string",
  • "operator": "equals",
  • "value": null
}

TicketFieldAccount

name
required
string
accountId
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
{
  • "name": "Line Shop",
  • "accountId": "string",
  • "channel": "line"
}

ListTicketsRequest

statuses
Array of strings
Items Enum: "open" "closed"

Omit to return open tickets only. Send both values for all tickets.

conversationId
string

Restrict to tickets belonging to this conversation. Sent on its own, returns the conversation's full history — open, closed, and reopened tickets. Add statuses to narrow the result.

accountIds
Array of strings
assigneeUserIds
Array of strings
handledByUserIds
Array of strings
resolutionStatuses
Array of strings
Items Enum: "none" "human" "automation" "ai_full_resolution" "ai_partial_resolution"
unassigned
boolean

When true, only tickets with no assignee. Cannot be combined with assigneeUserIds.

isFollowUp
boolean
isSpam
boolean

Defaults to false (non-spam tickets).

object (AdvancedFilters)

Optional extra filters on labels, ticket fields, waiting time, and opened-at.

object (LabelFilter)
Array of objects (TicketFieldFilter)
object (WaitingForReplyFilter)

Tickets where the buyer has been waiting at least this many minutes.

object (OpenedAtFilter)
{
  • "statuses": [
    • "open"
    ],
  • "unassigned": true
}

AdvancedFilters

object (LabelFilter)
operator
required
string
Enum: "contains_any" "contains_all" "does_not_contain_any"
labelIds
required
Array of strings [ 1 .. 20 ] items
Array of objects (TicketFieldFilter)
Array
ticketFieldId
required
string
operator
required
string
Enum: "equal" "not_equal" "starts_with" "text_contains" "is_one_of" "is_not_one_of" "contains_any" "contains_all" … 6 more
value
any

A single value, or an array for is_one_of, is_not_one_of, and contains_* operators. An array holds at most 20 values.

min
any

Lower bound for between.

max
any

Upper bound for between.

object (WaitingForReplyFilter)

Tickets where the buyer has been waiting at least this many minutes.

value
required
integer [ 1 .. 43200 ]
object (OpenedAtFilter)
operator
required
string
Enum: "between" "greater_than" "less_than"
value
string <date-time>

Used with greater_than and less_than.

min
string <date-time>

Used with between.

max
string <date-time>

Used with between.

{
  • "labels": {
    • "operator": "contains_any",
    • "labelIds": [
      ]
    },
  • "ticketFields": [
    • {
      }
    ],
  • "waitingForReply": {
    • "value": 30
    },
  • "openedAt": {
    • "operator": "between",
    • "value": "2019-08-24T14:15:22Z",
    • "min": "2019-08-24T14:15:22Z",
    • "max": "2019-08-24T14:15:22Z"
    }
}

LabelFilter

operator
required
string
Enum: "contains_any" "contains_all" "does_not_contain_any"
labelIds
required
Array of strings [ 1 .. 20 ] items
{
  • "operator": "contains_any",
  • "labelIds": [
    • "string"
    ]
}

TicketFieldFilter

ticketFieldId
required
string
operator
required
string
Enum: "equal" "not_equal" "starts_with" "text_contains" "is_one_of" "is_not_one_of" "contains_any" "contains_all" … 6 more
value
any

A single value, or an array for is_one_of, is_not_one_of, and contains_* operators. An array holds at most 20 values.

min
any

Lower bound for between.

max
any

Upper bound for between.

{
  • "ticketFieldId": "string",
  • "operator": "equal",
  • "value": null,
  • "min": null,
  • "max": null
}

WaitingForReplyFilter

value
required
integer [ 1 .. 43200 ]
{
  • "value": 30
}

OpenedAtFilter

operator
required
string
Enum: "between" "greater_than" "less_than"
value
string <date-time>

Used with greater_than and less_than.

min
string <date-time>

Used with between.

max
string <date-time>

Used with between.

{
  • "operator": "between",
  • "value": "2019-08-24T14:15:22Z",
  • "min": "2019-08-24T14:15:22Z",
  • "max": "2019-08-24T14:15:22Z"
}

UpdateTicketFieldRequest

ticketId
required
string
ticketFieldId
required
string
newValue
required
any

Type depends on the field's dataType. Send null to unset.

ticketFieldsUpdatedAt
string <date-time>

The ticketFieldsUpdatedAt you currently hold. A mismatch results in 409. Omit if the ticket has never had a field update.

{
  • "ticketId": "<id>",
  • "ticketFieldId": "<id>",
  • "ticketFieldsUpdatedAt": "<timestamp>",
  • "newValue": "SO-10482"
}

UpdateTicketAssigneesRequest

Array ([ 1 .. 50 ] items)
ticketId
required
string

The ticket to assign.

assigneeUserId
required
string <uuid>

The user to assign the ticket to.

[
  • {
    • "ticketId": "<id>",
    • "assigneeUserId": "<id>"
    }
]

UpdateTicketAssigneeItem

ticketId
required
string

The ticket to assign.

assigneeUserId
required
string <uuid>

The user to assign the ticket to.

{
  • "ticketId": "string",
  • "assigneeUserId": "11d6662f-b88d-444d-8712-eda11b5a0eed"
}

CloseTicketRequest

ticketId
required
string
handledBy
string <uuid>

The user who handled the ticket.

isConverted
boolean

Whether the conversation led to a conversion.

totalRevenue
number >= 0

Conversion revenue recorded on close.

currency
string

ISO 4217 code for totalRevenue.

object

Field values to set when closing, keyed by field id.

property name*
additional property
any
{
  • "ticketId": "<id>",
  • "handledBy": "<id>",
  • "isConverted": true,
  • "totalRevenue": 1890,
  • "currency": "THB"
}

Channel

string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
"line"

ConversationListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Conversation)
Array
id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
displayName
required
string
contactId
required
string
referenceId
required
string

The channel's own conversation id.

labelIds
required
Array of strings
isSpam
required
boolean
unreadMsgCnt
required
integer
allMsgCnt
required
integer
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

emailSubject
string

Present on email channels only.

lastRecipientMsgDate
string <date-time>
object
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Conversation

id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
displayName
required
string
contactId
required
string
referenceId
required
string

The channel's own conversation id.

labelIds
required
Array of strings
isSpam
required
boolean
unreadMsgCnt
required
integer
allMsgCnt
required
integer
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
accountId
string

Present unless expand includes account.

object

Replaces accountId when expand includes account.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
emailSubject
string

Present on email channels only.

lastRecipientMsgDate
string <date-time>
object
snippet
string
sentByRecipient
boolean
sentAt
string <date-time>
{
  • "id": "<id>",
  • "accountId": "<id>",
  • "channel": "line",
  • "displayName": "Somchai",
  • "contactId": "<id>",
  • "referenceId": "<channelConversationId>",
  • "labelIds": [
    • "<id>"
    ],
  • "isSpam": false,
  • "unreadMsgCnt": 2,
  • "allMsgCnt": 14,
  • "lastMsg": {
    • "snippet": "Thanks for your help",
    • "sentByRecipient": true,
    • "sentAt": "<timestamp>"
    },
  • "createdAt": "<timestamp>",
  • "updatedAt": "<timestamp>"
}

MarkConversationReadRequest

conversationId
required
string
{
  • "conversationId": "<id>"
}

UpdateConversationLabelsRequest

Any of
addLabels
required
Array of strings [ 1 .. 10 ] items
conversationId
required
string
removeLabels
Array of strings <= 10 items
{
  • "removeLabels": [
    • "<id>"
    ],
  • "conversationId": "<id>",
  • "addLabels": [
    • "<id>"
    ]
}

ContactResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
object (Contact)
id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": {
    • "id": "<id>",
    • "fullName": "Somchai Srisuk",
    • "firstName": "Somchai",
    • "lastName": "Srisuk",
    • "email": "[email protected]",
    • "phone": "+66812345678",
    • "createdAt": "<timestamp>",
    • "updatedAt": "<timestamp>"
    }
}

ContactListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Contact)
Array
id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

ContactSearchResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (ContactSearchHit)
Array
id
required
string
fullName
required
string
matchedFields
required
Array of strings

Fields that matched the query.

phone
string
email
string <email>
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

ContactSearchHit

id
required
string
fullName
required
string
matchedFields
required
Array of strings

Fields that matched the query.

phone
string
email
string <email>
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
{
  • "id": "<id>",
  • "fullName": "Somchai Srisuk",
  • "email": "[email protected]",
  • "phone": "+66812345678",
  • "matchedFields": [
    • "fullName"
    ]
}

ContactFieldListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (ContactField)
Array
id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 1 more
dataType
required
string
Enum: "number" "boolean" "string" "date"
active
required
boolean
sortOrder
required
integer

Display order among contact fields.

description
string
Array of objects (SelectOption)

Nested options for select fields. Maximum depth 4.

regex
string

Validation pattern for free-text values.

maxItems
integer

Maximum number of values when the field accepts more than one.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Contact

id
required
string
fullName
required
string
firstName
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
lastName
string
email
string <email>
phone
string
secondaryPhones
Array of strings
secondaryEmails
Array of strings <email> [ items <email > ]
addresses
Array of strings
note
string
object

Custom field values keyed by field id.

property name*
additional property
any
customFieldsUpdatedAt
string <date-time>

When a custom field was last written. Pass this back as updatedAt when updating a field. If this is absent, pass the contact's updatedAt instead.

{
  • "id": "<id>",
  • "fullName": "Somchai Srisuk",
  • "firstName": "Somchai",
  • "lastName": "Srisuk",
  • "email": "[email protected]",
  • "phone": "+66812345678",
  • "createdAt": "<timestamp>",
  • "updatedAt": "<timestamp>"
}

ContactField

id
required
string
name
required
string
displayType
required
string
Enum: "text" "multiline_text" "integer" "date" "datetime" "select" "file" "yes_no" … 1 more
dataType
required
string
Enum: "number" "boolean" "string" "date"
active
required
boolean
sortOrder
required
integer

Display order among contact fields.

description
string
Array of objects (SelectOption)

Nested options for select fields. Maximum depth 4.

Array
required
string or number
children
Array of objects (SelectOption)

Nested options for cascading selects.

regex
string

Validation pattern for free-text values.

maxItems
integer

Maximum number of values when the field accepts more than one.

{
  • "id": "<id>",
  • "name": "Loyalty tier",
  • "displayType": "select",
  • "dataType": "string",
  • "active": true,
  • "sortOrder": 1
}

ListContactsRequest

search
string <= 100 characters

Free-text search across name, email, and phone.

updatedAfter
string <date-time>

Only contacts updated on or after this time.

updatedBefore
string <date-time>

Only contacts updated before this time.

{
  • "search": "somchai"
}

CreateContactRequest

firstName
required
string <= 50 characters
lastName
string <= 50 characters
email
string <email> <= 100 characters
phone
string <= 15 characters

Primary phone number in E.164 format.

{
  • "firstName": "Somchai",
  • "lastName": "Srisuk",
  • "email": "[email protected]",
  • "phone": "+66812345678"
}

UpdateContactFieldRequest

contactId
required
string
contactFieldId
required
string
value
required
any

Type depends on the field's dataType. Send null to unset.

updatedAt
required
string <date-time>

The customFieldsUpdatedAt you currently hold, or the contact's updatedAt if customFieldsUpdatedAt is absent. An out-of-date value is rejected with 409.

{
  • "contactId": "<id>",
  • "contactFieldId": "<id>",
  • "updatedAt": "<timestamp>",
  • "value": "VIP"
}

MessageListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Message)
Array
id
required
string
conversationId
required
string
messageDirection
required
string
Enum: "inbound" "outbound"

inbound when the contact sent the message, outbound when the store sent it.

messageCategory
required
string
Value: "message"

Customer-facing messages are message.

sentAt
required
string <date-time>
text
string

Payload text, when the message has any.

images
Array of strings <uri> [ items <uri > ]

Image URLs, when the message has any.

type
string
Enum: "auto_response" "human_agent" "one_time_noti" "seller_only_messages" "deferred"

How the message was produced, when set.

object

Channel sender metadata, when present.

object

Present when expand includes conversation.

object

Present when expand includes account.

object

Present when expand includes sentBy.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Message

id
required
string
conversationId
required
string
messageDirection
required
string
Enum: "inbound" "outbound"

inbound when the contact sent the message, outbound when the store sent it.

messageCategory
required
string
Value: "message"

Customer-facing messages are message.

sentAt
required
string <date-time>
text
string

Payload text, when the message has any.

images
Array of strings <uri> [ items <uri > ]

Image URLs, when the message has any.

type
string
Enum: "auto_response" "human_agent" "one_time_noti" "seller_only_messages" "deferred"

How the message was produced, when set.

object

Channel sender metadata, when present.

property name*
additional property
any
object

Present when expand includes conversation.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
object

Present when expand includes account.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
object

Present when expand includes sentBy.

userId
required
string
name
required
string
roleId
string
{
  • "id": "<id>",
  • "conversationId": "<id>",
  • "text": "Where is my order?",
  • "messageDirection": "inbound",
  • "messageCategory": "message",
  • "sentAt": "<timestamp>"
}

MessageSearchResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (MessageSearchHit)
Array
id
required
string
conversationId
required
string
text
required
string
sentByRecipient
required
boolean

true when the contact sent the message.

sentAt
required
string <date-time>
score
required
number

Relevance score for this hit.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

MessageSearchHit

id
required
string
conversationId
required
string
text
required
string
sentByRecipient
required
boolean

true when the contact sent the message.

sentAt
required
string <date-time>
score
required
number

Relevance score for this hit.

{
  • "id": "<id>",
  • "conversationId": "<id>",
  • "text": "cancel my order",
  • "sentByRecipient": true,
  • "sentAt": "<timestamp>",
  • "score": 4
}

MessageRelatedSeller

userId
required
string
name
required
string
roleId
string
{
  • "userId": "<id>",
  • "name": "Nadia Prasert",
  • "roleId": "string"
}

InternalCommentListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (InternalComment)
Array
id
required
string
conversationId
required
string
accountId
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
object

Present when expand includes conversation.

object

Present when expand includes account.

authorUserId
string
object
sentAt
string <date-time>
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

InternalComment

id
required
string
conversationId
required
string
accountId
required
string
createdAt
required
string <date-time>
updatedAt
required
string <date-time>
object

Present when expand includes conversation.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
object

Present when expand includes account.

id
required
string
name
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
authorUserId
string
object
text
string
string or MessageRelatedSeller (object)

Author user id. When expand includes createdBy this is { userId, name, roleId }, or the raw id if that user has no record on the store.

tagText
string
mediaUrl
string <uri>
sentAt
string <date-time>
{
  • "id": "<id>",
  • "conversationId": "<id>",
  • "accountId": "<id>",
  • "noteData": {
    • "text": "Follow up tomorrow",
    • "createdBy": "<id>"
    },
  • "sentAt": "<timestamp>",
  • "createdAt": "<timestamp>",
  • "updatedAt": "<timestamp>"
}

SendPlainTextMessageRequest

conversationId
required
string
text
required
string [ 1 .. 1000 ] characters
{
  • "conversationId": "<id>",
  • "text": "Thanks for your order!"
}

CreateInternalCommentRequest

conversationId
required
string
text
required
string non-empty

Comment body. Tag a user with <@userId:name@> — userId from GET /users, and their display name.

{
  • "conversationId": "<id>",
  • "text": "Can you take this, <@3f2a9c1e-8b4d-4e71-9a06-2c8f1b7d4e90:John Smith@>?"
}

EmailAddress

email
required
string <email>
name
string

Display name shown beside the address.

{}

SendNewEmailRequest

accountId
required
string

Gmail or Outlook account to send from. Use an id from GET /accounts.

required
Array of objects (EmailAddress) [ 1 .. 50 ] items

Primary recipients.

Array ([ 1 .. 50 ] items)
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Blind carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

subject
string

Subject line.

text
string

Plain text body.

html
string

HTML body.

{
  • "accountId": "<id>",
  • "to": [],
  • "subject": "Your order is on the way",
  • "text": "Your order is on the way."
}

SendEmailRequest

conversationId
required
string

Gmail or Outlook conversation to send on.

required
Array of objects (EmailAddress) [ 1 .. 50 ] items

Primary recipients.

Array ([ 1 .. 50 ] items)
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

Array of objects (EmailAddress)

Blind carbon-copy recipients.

Array
email
required
string <email>
name
string

Display name shown beside the address.

subject
string

Subject line.

text
string

Plain text body.

html
string

HTML body.

{
  • "conversationId": "<id>",
  • "to": [],
  • "subject": "Re: Your order",
  • "html": "<p>Your order is on the way.</p>"
}

SendWhatsappTemplateRequest

conversationId
required
string

WhatsApp conversation to send on.

templateId
required
string

Template id from GET /accounts/whatsapp-templates.

variables
Array of strings

Positional body values for a template whose body contains {{1}}, {{2}}, and so on, in that order.

{
  • "conversationId": "<id>",
  • "templateId": "<id>",
  • "variables": [
    • "Nadia",
    • "SO-10482"
    ]
}

SendNewWhatsappTemplateRequest

accountId
required
string

WhatsApp account to send from. Use an id from GET /accounts.

phoneNumber
required
string

Recipient phone number in E.164 format.

templateId
required
string

Template id from GET /accounts/whatsapp-templates.

variables
Array of strings

Positional body values for a template whose body contains {{1}}, {{2}}, and so on, in that order.

{
  • "accountId": "<id>",
  • "phoneNumber": "+66812345678",
  • "templateId": "<id>",
  • "variables": [
    • "Nadia",
    • "SO-10482"
    ]
}

UserListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (User)
Array
id
required
string
name
string

Absent on incomplete invitations.

userId
string <uuid>

Absent for invited seats that have not yet been accepted.

roleId
string

Present unless expand includes role.

object

Replaces roleId when expand includes role.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

User

id
required
string
name
string

Absent on incomplete invitations.

userId
string <uuid>

Absent for invited seats that have not yet been accepted.

roleId
string

Present unless expand includes role.

object

Replaces roleId when expand includes role.

id
required
string
name
required
string
Enum: "admin" "owner" "chat-rep" "ai-bot"
{
  • "id": "<id>",
  • "userId": "<id>",
  • "name": "Nadia Prasert",
  • "roleId": "<id>"
}

LabelListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Label)
Array
id
required
string
label
required
string
color
required
string
fbAdId
string
whatsAppAdId
string
metaPostId
string
whatsAppBroadcastId
string
isSystemLabel
boolean
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Label

id
required
string
label
required
string
color
required
string
fbAdId
string
whatsAppAdId
string
metaPostId
string
whatsAppBroadcastId
string
isSystemLabel
boolean
{
  • "id": "<id>",
  • "label": "VIP",
  • "color": "#09c8ab"
}

TeamListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Team)
Array
id
required
string
name
required
string
description
string
Array of strings or RelatedUser (object)

User ids by default. When expand includes users, each member becomes { id, name } if that user exists on the store, otherwise the raw id.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Team

id
required
string
name
required
string
description
string
Array of strings or RelatedUser (object)

User ids by default. When expand includes users, each member becomes { id, name } if that user exists on the store, otherwise the raw id.

Array
One of
string
{
  • "id": "<id>",
  • "name": "Support",
  • "description": "Customer support team",
  • "users": [
    • "<id>"
    ]
}

AccountListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (Account)
Array
id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
name
string

Display name of the account as shown on the channel.

status
string
Enum: "healthy" "unhealthy" "disconnected"

Absent on accounts that have never reported a status.

lastAuthorizedAt
string <date-time>

When the account was last authorised against the channel.

object

Channel metadata. Which fields are present depends on the channel.

{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

Account

id
required
string
channel
required
string (Channel)
Enum: "line" "instagram" "facebook" "lazada" "shopee" "tiktok" "drunken_lullabies" "whatsapp" … 3 more
name
string

Display name of the account as shown on the channel.

status
string
Enum: "healthy" "unhealthy" "disconnected"

Absent on accounts that have never reported a status.

lastAuthorizedAt
string <date-time>

When the account was last authorised against the channel.

object

Channel metadata. Which fields are present depends on the channel.

userName
string

Channel handle, for example a LINE basic ID.

shopId
string

Marketplace shop identifier, marketplace channels only.

country
string
followerCount
integer

Follower or friend count, where the channel reports one.

{
  • "id": "<id>",
  • "channel": "line",
  • "name": "Line Shop",
  • "status": "healthy",
  • "lastAuthorizedAt": "<timestamp>",
  • "metadata": {
    • "userName": "@mpe9992b",
    • "country": "TH",
    • "followerCount": 44117
    }
}

WhatsappTemplateListResponse

requestId
required
string
requestReceivedAt
required
string
storeId
required
string
required
Array of objects (WhatsappTemplate)
Array
id
required
string

Template id. Pass this as templateId when sending.

name
required
string
parameter_format
required
string

How body placeholders are filled. POSITIONAL uses {{1}}, {{2}}, in order.

language
required
string

Template language code.

status
required
string (WhatsappTemplateStatus)
Enum: "APPROVED" "ARCHIVED" "DELETED" "DISABLED" "FLAGGED" "IN_APPEAL" "LIMIT_EXCEEDED" "LOCKED" … 5 more
category
required
string (WhatsappTemplateCategory)
Enum: "MARKETING" "UTILITY"
required
Array of objects (WhatsappTemplateComponent)

Template parts. A BODY part may contain {{1}}, {{2}}, and so on. Pass those values as variables when you send the template.

object (WhatsappTemplateQualityScore)
{
  • "requestId": "<requestId>",
  • "requestReceivedAt": "1757580000000",
  • "storeId": "<id>",
  • "data": [
    • {
      }
    ]
}

WhatsappTemplateStatus

string (WhatsappTemplateStatus)
Enum: "APPROVED" "ARCHIVED" "DELETED" "DISABLED" "FLAGGED" "IN_APPEAL" "LIMIT_EXCEEDED" "LOCKED" … 5 more
"APPROVED"

WhatsappTemplateCategory

string (WhatsappTemplateCategory)
Enum: "MARKETING" "UTILITY"
"MARKETING"

WhatsappTemplate

id
required
string

Template id. Pass this as templateId when sending.

name
required
string
parameter_format
required
string

How body placeholders are filled. POSITIONAL uses {{1}}, {{2}}, in order.

language
required
string

Template language code.

status
required
string (WhatsappTemplateStatus)
Enum: "APPROVED" "ARCHIVED" "DELETED" "DISABLED" "FLAGGED" "IN_APPEAL" "LIMIT_EXCEEDED" "LOCKED" … 5 more
category
required
string (WhatsappTemplateCategory)
Enum: "MARKETING" "UTILITY"
required
Array of objects (WhatsappTemplateComponent)

Template parts. A BODY part may contain {{1}}, {{2}}, and so on. Pass those values as variables when you send the template.

Array
type
required
string
Enum: "GREETING" "HEADER" "BODY" "FOOTER" "BUTTONS" "CAROUSEL" "LIMITED_TIME_OFFER" "CALL_PERMISSION_REQUEST"
format
string

Present on some headers, for example TEXT or IMAGE.

text
string

Component text. Body text may contain {{1}}, {{2}}, and so on.

Array of objects (WhatsappTemplateButton)
object (WhatsappTemplateQualityScore)
score
required
string

Template quality. Common values are GREEN, YELLOW, RED, and UNKNOWN.

date
number

When WhatsApp recorded this score, as a Unix timestamp.

{
  • "id": "<id>",
  • "name": "order_update",
  • "parameter_format": "POSITIONAL",
  • "language": "en",
  • "status": "APPROVED",
  • "category": "UTILITY",
  • "components": [
    • {
      }
    ]
}

WhatsappTemplateComponent

type
required
string
Enum: "GREETING" "HEADER" "BODY" "FOOTER" "BUTTONS" "CAROUSEL" "LIMITED_TIME_OFFER" "CALL_PERMISSION_REQUEST"
format
string

Present on some headers, for example TEXT or IMAGE.

text
string

Component text. Body text may contain {{1}}, {{2}}, and so on.

Array of objects (WhatsappTemplateButton)
Array
type
required
string
Enum: "PHONE_NUMBER" "URL" "QUICK_REPLY" "COPY_CODE"
text
required
string
url
string

Present on URL buttons.

phone_number
string

Present on PHONE_NUMBER buttons.

{
  • "type": "GREETING",
  • "format": "string",
  • "text": "string",
  • "buttons": [
    • {
      }
    ]
}

WhatsappTemplateButton

type
required
string
Enum: "PHONE_NUMBER" "URL" "QUICK_REPLY" "COPY_CODE"
text
required
string
url
string

Present on URL buttons.

phone_number
string

Present on PHONE_NUMBER buttons.

{
  • "type": "PHONE_NUMBER",
  • "text": "string",
  • "url": "string",
  • "phone_number": "string"
}

WhatsappTemplateQualityScore

score
required
string

Template quality. Common values are GREEN, YELLOW, RED, and UNKNOWN.

date
number

When WhatsApp recorded this score, as a Unix timestamp.

{
  • "score": "GREEN",
  • "date": 0
}