> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tawked.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Symptom, cause and fix for the problems integrations meet most: codes that do not arrive, refused keys, WhatsApp sends that fail, webhooks that do not verify, and the balance.

Start from what you see. Each answer names the `error` string, the likely cause and the fix. The [Errors](/errors) page lists every code, and every response carries an `X-Request-Id` to quote to [support@tawked.com](mailto:support@tawked.com).

## Codes

<AccordionGroup>
  <Accordion title="The code never arrives on the phone">
    Open **Verify → Verifications** and find the verification by its id or your `reference`. Its status says what happened.

    * **A test key** sends only to the phone the account owner verified at signup. Any other number answers `422 sandbox_unverified_destination`. See [Testing and the sandbox](/sandbox).
    * **`sent`, but nothing on the phone**: check the number in the log. Tawked accepts five Saudi mobile formats and normalises them; a typo in a valid format still goes to the wrong phone. See [Numbers, language and the SMS](/destinations).
    * **`failed` right away**: the send failed and was refunded. Retry with the same `Idempotency-Key`.
    * **Not in the log at all**: the call was refused before a send. Read the `error` your server got back.

    The SMS comes from Tawked's sender ID, and its text names your application. Users looking for a message from your own brand name may miss it: tell them what to expect on your code screen.
  </Accordion>

  <Accordion title="403 account_not_active on start, but WhatsApp sends work">
    The application is still in review. Codes wait for approval; WhatsApp messages do not. **Application settings → Details** shows the review. Meanwhile, build and test with a test key, which works in review.

    The same answer on every call means the application is rejected or suspended, a partner disabled it, or the account is banned.
  </Accordion>

  <Accordion title="429 rate_limited for one number">
    That number reached the hourly cap on codes, 5 per hour by default, counted across your applications. Tell the user to wait. Do not raise the cap to work around one user: it is what keeps a pumping attack cheap. See [Reliability and limits](/reliability).
  </Accordion>

  <Accordion title="403 application_paused">
    The protection guard paused the application after a spike in new destinations, which is what an SMS pumping attack looks like. The pause is raised under **Alerts**, and the owner or an admin resumes it in the console. If the spike was an attack, set a daily spend cap or lower the caps under **Verify → Settings** first.
  </Accordion>

  <Accordion title="429 sandbox_quota_exceeded">
    A test key has 10 sends per application in its lifetime. Move to a live key once the application is approved, and record the documented responses as fixtures for your unit tests instead of sending.
  </Accordion>
</AccordionGroup>

## Keys

<AccordionGroup>
  <Accordion title="401 unauthorized">
    The key is missing, mistyped or revoked, or it is an application key (`tk_live_`, `tk_test_`) on the partner endpoints, `/v1/partner/...`, which take only a partner key. Check that the header is `Authorization: Bearer <key>` or `x-api-key: <key>` and that no whitespace came with the key from your secret store.

    `401 key_expired` is a key past its expiry. Create a new one.
  </Accordion>

  <Accordion title="403 insufficient_scope">
    The key's access does not cover the call. The `WWW-Authenticate` header names the scope the call needs, for example `scope="verify:send"`. Access cannot be changed after a key is created: create a key with the access you need, deploy it, then revoke the old one. See [Key access](/authentication#key-access).
  </Accordion>

  <Accordion title="403 ip_not_allowed">
    The call came from an address outside the key's IP allowlist. Your server's outgoing address may differ from the one you expected behind a NAT or a load balancer. Add it, or create a key with the right list.
  </Accordion>

  <Accordion title="429 too_many_requests">
    More than 120 requests in one minute on one key. The window is a fixed 60 seconds and the answer carries no `Retry-After`: wait, then retry within the minute. If you poll `GET /v1/verify/{id}` for outcomes, use [webhooks](/webhooks) instead.
  </Accordion>
</AccordionGroup>

## WhatsApp messages

<AccordionGroup>
  <Accordion title="403 live_key_required">
    WhatsApp has no sandbox, so a test key cannot send or read on `/v1/whatsapp/...`. Use the application's live key with Notifications access. A live key works while the application is in review.
  </Accordion>

  <Accordion title="409 no_whatsapp_number or 409 number_disconnected">
    `no_whatsapp_number`: nothing is connected yet. The owner or an admin connects the number under **Notifications → Overview**.

    `number_disconnected`: the number is attached, but Meta does not report it as connected now. **Notifications → Overview** shows its health and what Meta asks of it.
  </Accordion>

  <Accordion title="422 unknown_template or 422 template_not_approved">
    The `template` name must match a template on the number's WhatsApp Business Account exactly, and Meta must have approved it. `GET /v1/whatsapp/templates` lists them with their status. A template that exists in several languages also needs `lang`. See [Templates](/whatsapp/templates).
  </Accordion>

  <Accordion title="422 invalid_params">
    A placeholder value is missing, empty, unexpected or too long, or a template with an image header was sent without `header.image`. The `message` names what to fix. The template's `placeholders` in `GET /v1/whatsapp/templates` say what each one needs.
  </Accordion>

  <Accordion title="422 consent_opted_out">
    The customer asked this application to stop its marketing: they wrote a stop word to the number, or turned off its offers in WhatsApp. Nothing was sent or charged. Do not retry. Utility and authentication templates still reach them. See [Sending messages](/whatsapp/messages).
  </Accordion>

  <Accordion title="The message was accepted but shows failed">
    `202` means Meta accepted the message. Delivery can still fail later: the status becomes `failed`, a `message.failed` event carries Meta's reason in `error`, and the fee is refunded. **Notifications → Messages** shows each message's history.
  </Accordion>
</AccordionGroup>

## Webhooks

<AccordionGroup>
  <Accordion title="The signature never matches">
    * Compute the HMAC over the **raw** body, before any JSON parsing. A framework that parses and re-serialises the body changes its bytes.
    * The signed string is `{tawked-timestamp}.{raw body}`, with a dot between, and the timestamp is in milliseconds as sent.
    * Use the application's current signing secret. Rotating it on the Webhooks page changes what every later delivery is signed with.
    * Compare the hex digests in constant time.

    See [Webhooks](/webhooks#verifying-the-signature) for code in Node.js, PHP and Python.
  </Accordion>

  <Accordion title="Events do not arrive">
    Open the Webhooks page under **Application settings** and its **Delivery log**. Each delivery shows its attempts and your server's last answer.

    * The URL must be `https://` on a public host. A redirect is not followed.
    * Your endpoint must answer `2xx` within 5 seconds. Acknowledge first, then do the work.
    * A failed delivery is tried 5 times in all: at once, then after 1 minute, 5 minutes, 30 minutes and 2 hours. After that, **Send again** in the log queues it anew.
    * **Send a test event** checks the whole path in one click.
  </Accordion>

  <Accordion title="The same event arrived twice">
    Expected: a retry after a slow answer, or **Send again**, delivers an event you may already have. Make the handler idempotent on `data.id` together with the `event` name.
  </Accordion>
</AccordionGroup>

## The balance

<AccordionGroup>
  <Accordion title="402 insufficient_credits">
    The balance is below the price of this send. Nothing was sent or charged. The body carries `balance_halalas` and `price_halalas`. Top up under **Wallet**, by card or bank transfer, and set a **Low-balance alert** under **Wallet → Balance** so it does not happen again.
  </Accordion>

  <Accordion title="429 spend_cap_reached">
    Today's charges for this application plus this send would pass the daily spend cap you set under **Verify → Settings**. Days are Riyadh calendar days. Raise the cap if the traffic is real.
  </Accordion>
</AccordionGroup>

Still stuck? Write to [support@tawked.com](mailto:support@tawked.com) with the `X-Request-Id`, the time of the call and the `error` you got.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.