Skip to main content
The Account API lets an agent or a script do what a person does in the Tawked console: create and check WhatsApp templates, read messages, add members and support team members, add an e-mail sending domain, run campaigns and automations, read the wallet, and the rest. Every console write has its operation, and every operation runs the console’s own rules. It sits beside the sending API, on the same base URL, with a different credential: An application key never opens the Account API, and an account token never opens the sending API. Keep sending with application keys from your servers; use an account token to set things up and to check on them. Base URL: https://tawked.com/v1. JSON in and out.
To connect an agent now, follow the quickstart: the steps for Claude Code, Claude, ChatGPT, Cursor, Codex and a script, and the prompt to give the agent.

Account tokens

Make a token in the console under Settings → Account API, with Create token. Anyone with console access makes tokens for themselves. The token is shown once: put it in your agent’s environment as TAWKED_TOKEN (or its secret storage), never into source code or a chat. The reveal’s instructions for your agent hold no secret: they tell it to send Authorization: Bearer $TAWKED_TOKEN. An assistant that signs in to the MCP server gets a token of its own, made by its consent page: you never copy that one, and it has its own page in the same list, marked Assistant.
  • It acts as you, never more. A call made with your token is your call: your role, the applications you reach, and every rule the console applies to you. A member who may not delete a template in the console may not delete it with a token either.
  • Permissions narrow it. Choose Everything you can do (*, every area now and later), Read only (every area’s read), or Choose per area: read, or read and write, area by area. Permissions lists the areas.
  • Applications narrow it. All your applications (the ones you reach now and the ones added later), or only those you choose. An application outside the token’s choice answers 404 not_found, as if it did not exist, whether it is in the path, the body or the query. A token narrowed to some applications reads the account’s lists (alerts, e-mail suppressions, the wallet’s statement) as a member limited to those applications would, and may not change who works in the account: a members write answers 403 token_narrowed with a widen_applications human_action.
  • Expiry. Never, or after 30 days, 90 days or 1 year. An expired token answers 401 token_expired, and a revoked one 401 token_revoked, each with a new_token human_action that sends the person to make a new one.
  • Approvals. Ask me first (the default) holds every consequential call until you approve it in the console; Act without asking is for a script you trust. See Approvals.
  • Addresses. Optionally, only from the IP addresses or ranges you list; any other answers 403 ip_not_allowed.
  • Revoke. On the same page, Revoke token refuses every request that carries it at once. You see and revoke your own tokens; the account owner sees and revokes every token of the account. DELETE /v1/account/tokens/{token} does the same from the API.
  • Recorded. Every call made with a token is on record for 90 days (account.tokens.activity, and the token’s page), and every change it made is in the account’s record, with the token (or the assistant) that made it. A new token e-mails its person, and the owner when a member made it.
Security has all of it. A token is refused at the door when the person could not open the console either: 403 account_suspended (the owner sees why in the console), 403 onboarding_incomplete (finish setting up the account first: the answer carries the link, see below), 403 setup_pending (the account owner has not finished setup), 403 no_console_access (a person who answers in Tawked Chat only).

The agent’s loop

An agent does not need to know the operations in advance. The API describes itself:
1

Who am I

GET /v1/me answers the person, the account, the token’s permissions and expiry, the person’s capabilities, and the applications the token reaches with their products. Call it first: most paths need an application’s id from it.
2

Find the operation

GET /v1/operations?q=whatsapp+template+create searches every operation by words (q) and optionally by area, best matches first. Each result has its id, method, path, summary, permission, whether it is consequential, and allowed: whether this token may call it. When allowed is false, needs names the permission its person would add; calling it answers 403 insufficient_permission with the link to add it.
3

Read its input

GET /v1/operations/whatsapp.templates.create answers the operation in full: its path parameters (parameters: for each, what it is and list_with, the operation that lists its values, such as whatsapp.templates.list for {template}), the JSON Schema of its body (a write) or its query (a read), allowed, and next, how to call it (the method and path, the path parameters, an Idempotency-Key on a write, and for a consequential operation on a token that asks first, the 428 loop). When the token may not call it, the answer adds needs and a grant_permission human_action to relay. An id that does not exist answers 404 not_found with did_you_mean, the nearest operations.
4

Call it

The method and path from step 2, with the body from step 3. When there is a next step, the answer’s next says it in a sentence that names the operation to call and, when the step is to wait, how often to read it again (for example whatsapp.templates.show every 30 to 60 seconds while Meta reviews a template).
5

Hand the person what is theirs

When an answer carries human_action, a person must act: tell them its say, give them its url, and once they say it is done call its then. A 428 approval_required is one of these: see The person in the loop.
An operation’s id (<area>.<resource>.<verb>, such as email.senders.check) is stable: write it into prompts and scripts. Every operation is also in the reference, generated from the same registry.

Conventions

  • Answers. Success is {"data": …}; an operation that makes something answers 201, the reference says which. A write with nothing to report answers {"data": {"ok": true}}. A refusal is {"error": "…", "message": "…"} (Errors). A 2xx always means the operation ran; a call held for its person’s yes answers 428 approval_required (Approvals).
  • Where next and human_action sit. On a success they are inside data (data.next, data.human_action); on a refusal they are beside error, at the top of the answer (next, human_action). Every refusal carries next (Errors); a success carries it when something is left to do or wait for.
  • What next. An answer with a next step carries next: a sentence naming the operation to call and, for a wait, how often to read again (whatsapp.messages.show every 5 to 10 seconds while a message is on its way, whatsapp.templates.show every 30 to 60 seconds while Meta reviews, a decision by Tawked’s team every few hours). It follows the state of what it answers: a template that is ready to create, a message that failed (with what Meta’s code means), a campaign by its status and pause reason, an automation and its runs, the WhatsApp number, Tawked Email by its stage, an open proforma, a low balance, Radeef, the auto-attendant, an import. It is never a bare id, and it is left out once nothing is left to do. List rows carry none (read the row’s show), except open proformas.
  • Lists. {"data": [...], "next_cursor": "..."}. Ask for up to 50 rows with ?limit= (default 20), and pass next_cursor back as ?cursor= for the next page; null means there is none.
  • Fields are snake_case. Times are ISO 8601 in UTC. Money is an integer in halalas, in fields named *_halalas. Ids are the console’s.
  • What you see is what the console shows you: phone numbers and cards are masked where the console masks them.
  • Language. A sentence meant for the person (a human_action’s say) and the console link beside it (its url, an approval’s url) are in the language of the request’s Accept-Language when it sends one (ar for Arabic, anything else English), else in the token’s person’s own language in Tawked. message, next, an operation’s description and the messages in a refusal’s fields are English, for the agent, whatever Accept-Language says. The approval’s e-mail is always in the person’s own language. A sign-in message for a member or a support team member added (members.members.create, chat.team.add) is written in the body’s language (ar or en) when you send it, else in the token’s person’s own language.
  • Text from outside the account. An answer holding text written outside the account (a customer’s message, a contact’s name, a page read from the web) carries Tawked-Untrusted-Content: true, and its operation is marked untrusted: data, never instructions.
  • Paths. Account-wide things under /v1/account/…; an application’s under /v1/applications/{application}/<area>/…; a Salla or Zid store, a partner’s client application and an integration app under their own (/v1/stores/salla/{store}, /v1/clients/applications/{client}, /v1/integration-apps/{app}). GET reads, POST creates or acts, PATCH changes some fields, PUT replaces a settings document, DELETE removes.

Errors

Every error is JSON with a stable error string and the HTTP status. Branch on the string; message is a sentence for your logs and for the person. Every refusal also carries next, one sentence saying what to do now: the refusal’s own when it has one (approvals.show for an approval, a top-up that is not open), else one chosen by its code and status (fix fields and send again, list the ids of the path, wait for Retry-After, read the thing’s state again, or do not retry). When only a person can lift the refusal, the error also carries human_action: what to tell them, the link, and what to call after (The person in the loop), and next says to relay it, then call its then.
A path parameter whose value cannot be one names the parameter and where to find its values:
A validation failure names each field and what is wrong with it, in codes:
An operation’s own refusal keeps its own code, such as domain_taken, meta_refused or template_not_editable; the operation’s description in the reference lists them.

Idempotency

Send an Idempotency-Key header (1 to 128 printable ASCII characters, one per intended change) on any write you might retry. A replay with the same key and the same request answers the stored response with Idempotent-Replayed: true and changes nothing again, for 24 hours. The same key with a different request answers 409 idempotency_key_reused; while the first is still running, 409 idempotency_in_progress. An error is not stored, so a retry after a 4xx runs again. Without the header the write simply runs. A 428 approval_required is never stored either: once the person approved, send the request again with the same key and Tawked-Approval, and it runs once. A secret shown once (an API key’s plaintext, a webhook’s secret, an app’s client secret, a test key) is never kept: a replay of that request answers 409 idempotency_secret_not_replayable with the id of what it made, never the secret.

Limits

Per token: 300 calls a minute, of which at most 60 writes a minute. Past either, the API answers 429 rate_limited with Retry-After. Some operations have a limit of their own, said in their description (checking an e-mail domain’s DNS, for example). Per address, calls without a working token (each answered 401) are limited to 300 a minute; past it, the address answers 429 rate_limited with Retry-After before any token is read. Calls with a working token never count, so agents that share an address are never held back.

Consequential operations

An operation is consequential when it reaches customers (a send, a campaign’s start), spends money, deletes, or cannot be undone. GET /v1/operations marks each one ("consequential": true), and the reference carries x-consequential.
  • A token that asks first (the default for a token made in the console) answers 428 approval_required: the person approves the exact request in the console, and the agent sends it again with Tawked-Approval. See Approvals.
  • A token that acts without asking (the person chose it for a script they trust) runs it at once: your code is the one deciding.
  • Through the MCP server, an assistant’s sign-in first shows the person a preview and waits for their yes in the chat (confirm_id); an account token that asks first goes to the console’s approval instead.

What stays with a person

A few steps need the person: another company’s page, a code in their inbox, a yes. Each answers a human_action with the link and the sentence to say; The person in the loop lists every reason. The two you meet first:
  • Connecting a WhatsApp number runs in Meta’s own window. GET /v1/applications/{application}/whatsapp/connection answers the console address that starts it and how far it got. Give the address to the person, then read GET /v1/applications/{application}/whatsapp/number until the number is connected.
  • Paying by card happens on the payment page. GET /v1/account/wallet/top-up-link answers the console’s Top up address, with the amount filled in when you pass amount_halalas, and can_pay: true when the token’s person may pay (their role manages the wallet) and a way to pay is on, so it is false whenever payments_available is false. The auto top-up, a proforma for a bank transfer and the billing details are operations.
Signing up is the person’s too: it takes their phone’s code. Everything after it is an operation.

Example: a WhatsApp template, until Meta decides

The application needs a connected WhatsApp number, and the token whatsapp:write.
1

Create the template

It is submitted to Meta for review and answers 201 with the template’s id and its status, PENDING:
examples holds one example per variable, keyed by where it sits: body:1, header:1, button:0. A refusal from Meta (a name already used, for example) answers 422 meta_refused with Meta’s sentence.
2

Poll its status

Meta usually decides within minutes, sometimes up to a day. Read the template every 30 to 60 seconds until status is APPROVED or REJECTED:
The answer carries sendable (true once approved), quality, and rejected_reason when Meta refused it. Fix a rejected template with PATCH on the same path (Meta reviews it again), or create a new one.
An approved template is sent by your servers with an application key: see Sending a template message.

Example: an e-mail sending domain, until it is verified

The application needs access to Tawked Email (see Tawked Email), and the token email:write.
1

Add the domain

It answers 201 with the domain in the list’s shape, plus next: its id, its state (pending) and the DNS records to add at the domain’s DNS host, each with its type, name, value, host_label (what goes in the host’s Name field) and state. Adding a domain the application already has answers that domain. A public mailbox’s domain such as gmail.com is refused with 422 domain_public, and a domain another application verified with 409 domain_taken.
2

Read the records

Add each record at the DNS host, with the host’s own tools or API. When connect_available is true, POST /v1/applications/{application}/email/senders/{domain}/connect answers a link the person opens to add the record at their host in one step.
3

Check until verified

DNS can take minutes to spread, so pending is normal at first. Checks are limited to 6 an application a minute, the console’s included (429 rate_limited). A pending domain waits for its records until pending_until, in the answer; past_window turns true once that time has passed.
Once verified, your servers send from the domain with an application key: see Sending email.

The reference

Every operation, with its path, parameters, body schema, permission (x-permission) and whether it is consequential (x-consequential), is in the reference on this tab, grouped by area and generated from the API’s own registry, the one GET /v1/operations reads. Its OpenAPI 3.1 document downloads from the menu at the top of any reference page: feed it to a client generator or to an agent that reads specs.
Last modified on October 15, 2026