> ## 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.

# Send a template message

> Sends an approved template to a Saudi mobile through the number attached to the application, and answers `202` with an id to track it. The fee, if any, is for the send; a failed send refunds it.



## OpenAPI

````yaml https://tawked.com/openapi.json post /v1/whatsapp/messages
openapi: 3.1.0
info:
  title: Tawked Verify API
  version: 2026-09
  summary: >-
    OTP verification for Saudi mobile numbers over SMS or WhatsApp, in two
    calls.
  description: >-
    Tawked is a Saudi OTP verification API: one request sends a code by SMS or
    WhatsApp to a Saudi mobile number, one request checks it. Built in Jeddah,
    billed only per delivered code. SAR 0.09 per delivered SMS code, prepaid;
    failed sends are not charged. Authenticate with a Bearer API key from the
    dashboard (tk_live_ for production, tk_test_ for the sandbox, tk_partner_
    for partners). Both /v1/... and /api/v1/... answer identically. Rate limit:
    120 requests per minute per key.
  termsOfService: https://tawked.com/en/terms
  license:
    name: Proprietary (Tawked terms of service)
    url: https://tawked.com/en/terms
  contact:
    name: Tawked support
    email: support@tawked.com
    url: https://tawked.com/en/contact
servers:
  - url: https://tawked.com
    description: Production (the sandbox is a tk_test_ key on the same host)
  - url: https://tawked.com/api
    description: The same API under /api/v1/..., kept for older integrations
security:
  - bearerKey: []
  - headerKey: []
tags:
  - name: Verify
    description: Send and check one-time codes.
  - name: WhatsApp
    description: >-
      Send approved WhatsApp templates through the number connected to an
      application; any approved application connects its own number from the
      dashboard.
externalDocs:
  description: Developer docs, with code samples
  url: https://tawked.com/en/docs
paths:
  /v1/whatsapp/messages:
    post:
      tags:
        - WhatsApp
      summary: Send a template message
      description: >-
        Sends an approved template to a Saudi mobile through the number attached
        to the application, and answers `202` with an id to track it. The fee,
        if any, is for the send; a failed send refunds it.
      operationId: whatsapp_send
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            A 1 to 128 printable-ASCII-character key you generate, scoped to
            your API key. Replaying the same key returns the original `202`
            response unchanged, with the response header `Idempotent-Replayed:
            true`: nothing is sent or charged again. A send that fails with
            `502` releases the key. Use one key per business event
            (`order-1042-received`) and retry a `502` or a network error with
            the same key.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                to:
                  type: string
                  description: >-
                    The destination: a Saudi mobile in any of the accepted
                    formats, stored as E.164.
                template:
                  type: string
                  description: >-
                    The template name exactly as WhatsApp Manager lists it, 1 to
                    512 characters. It must be approved on the number's WhatsApp
                    Business Account; authentication templates are refused
                    (one-time codes go through Verify).
                lang:
                  type: string
                  description: >-
                    The template language as Meta lists it (`ar`, `en`,
                    `en_US`). Required when the template exists in more than one
                    language.
                params:
                  type: object
                  description: >-
                    Placeholder values for the body: the keys are the
                    placeholder names of a named template (`{{name}}`), or
                    `"1"`, `"2"` for a positional one (`{{1}}`). Every
                    placeholder is required, extra keys are refused, values are
                    strings of 1 to 1024 characters. `GET
                    /v1/whatsapp/templates` lists the placeholders of each
                    template.
                header:
                  type: object
                  description: >-
                    `{ "text": "…" }`: the value of the text header's
                    placeholder. Required when the template has one, refused
                    otherwise; no newlines. Media headers are not supported yet.
                buttons:
                  type: array
                  description: >-
                    `[{ "index": 0, "parameter": "…" }]`: the dynamic suffix of
                    each URL button, one entry per button that has a
                    placeholder; `index` is the button's position among the
                    template's buttons.
                reference:
                  type: string
                  description: >-
                    Your own reference (order id, event id), 1 to 64 printable
                    characters. Echoed back, and searchable in the Messages log
                    of the console.
                application:
                  type: string
                  description: >-
                    Partner keys only: which of your provisioned applications is
                    sending, as on `start`.
              required:
                - to
                - template
      responses:
        '202':
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  to:
                    type: string
                  template:
                    type: string
                  lang:
                    type: string
                  reference:
                    type: string
                  price_halalas:
                    type: integer
                  created_at:
                    type: string
                    format: date-time
              examples:
                accepted:
                  summary: Accepted
                  value:
                    id: 0f4c9c0e-6d2b-4b8a-9c3e-7a1d2e3f4a5b
                    status: accepted
                    to: '+966550000000'
                    template: order_received_v1
                    lang: ar
                    reference: order-1042
                    price_halalas: 0
                    created_at: '2026-09-06T12:00:00+03:00'
        '400':
          description: >-
            `service_required`: Partner key used with no `application` (or
            `service`) in the request body.; `invalid_json`: The request body is
            not valid JSON.; `invalid_request`: start: `to` (and `phone`) both
            missing or blank. check: `id` or `code` missing.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - service_required
                          - invalid_json
                          - invalid_request
        '401':
          description: >-
            `unauthorized`: Missing, unknown, revoked, or mode-mismatched key;
            or an unknown/suspended partner.; `key_expired`: The key's
            `expires_at` has passed. Checked right after the key is recognised,
            before the IP allowlist or scope.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - unauthorized
                          - key_expired
        '402':
          description: >-
            Insufficient credits / `insufficient_credits`: The account's prepaid
            balance is below the channel price.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - insufficient_credits
              examples:
                insufficient_credits:
                  summary: Insufficient credits
                  value:
                    error: insufficient_credits
                    message: Insufficient credits
                    balance_halalas: 0
                    price_halalas: 9
        '403':
          description: >-
            `account_not_active`: The application is not approved yet, the
            account is banned, or a partner disabled this application. All four
            causes share this one response.; `ip_not_allowed`: The caller's IP
            does not match the key's `ip_allowlist` (exact IPs and/or CIDR
            ranges, IPv4 and IPv6).; `live_key_required`: A sandbox (tk_test_)
            key. WhatsApp has no sandbox yet; use the application's live key.;
            `whatsapp_not_enabled`: WhatsApp is turned off for this application.
            Message support if you think this is a mistake.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - account_not_active
                          - ip_not_allowed
                          - live_key_required
                          - whatsapp_not_enabled
        '404':
          description: >-
            `service_not_found`: Partner key, but `application`/`service` does
            not match any of that partner's provisioned applications.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - service_not_found
        '409':
          description: >-
            `no_whatsapp_number`: No WhatsApp number is connected to this
            application yet. Connect one from the application's WhatsApp tab in
            the dashboard (Getting started, step 1).; `number_disconnected`: The
            number is attached but not connected on Meta's side right now (its
            webhook is not claimed, or Meta reports a status other than
            CONNECTED). The console's WhatsApp page shows the number's health.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - no_whatsapp_number
                          - number_disconnected
        '422':
          description: >-
            Unknown template / `invalid_destination`: The destination does not
            match any accepted Saudi mobile format.; `unknown_template`: No
            template with that name (and language, when given) on the number's
            WhatsApp Business Account. `GET /v1/whatsapp/templates` lists what
            exists.; `template_not_approved`: The template exists but Meta has
            not approved it; `message` carries Meta's status (PENDING, REJECTED,
            PAUSED, …).; `template_category_not_allowed`: An AUTHENTICATION
            template. One-time codes go through the Verify API, not this
            endpoint.; `unsupported_template`: The template needs something this
            endpoint does not carry yet: a media header (image, video, document,
            location), a button or component that takes a non-text parameter
            (copy code, flow, catalog, carousel, limited-time offer), or named
            and positional placeholders mixed in the text. `message` says
            which.; `invalid_params`: A placeholder value is missing, empty,
            unexpected, longer than 1024 characters or contains a newline, a tab
            or another control character (Meta refuses those), or the template
            exists in several languages and `lang` was not given. `message`
            names the placeholder.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - invalid_destination
                          - unknown_template
                          - template_not_approved
                          - template_category_not_allowed
                          - unsupported_template
                          - invalid_params
              examples:
                unknown_template:
                  summary: Unknown template
                  value:
                    error: unknown_template
        '429':
          description: >-
            `too_many_requests`: Per-key rate limit exceeded (default 120
            requests/minute, fixed 60s window). Checked before authentication.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - too_many_requests
        '500':
          description: >-
            `internal_error`: An unhandled server error. Quote the X-Request-Id
            header if you report it.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - internal_error
        '502':
          description: >-
            `send_failed`: The delivery provider call threw. The charge is
            refunded automatically.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - send_failed
        '503':
          description: >-
            `templates_unavailable`: Meta did not answer the read of the
            number's template list, which both `GET /v1/whatsapp/templates` and
            a send need before anything happens. Nothing was sent or charged;
            retry shortly.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - properties:
                      error:
                        enum:
                          - templates_unavailable
components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: A stable machine-readable code; the docs list every one.
      additionalProperties: true
  securitySchemes:
    bearerKey:
      type: http
      scheme: bearer
      description: 'Authorization: Bearer <api key>'
    headerKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        The same API key, as a header, for clients that cannot set
        Authorization.

````