# Send a message (/docs/api-reference/whatsapp/sendWhatsappMessage)

> Send one message through phone_number_id to a customer, in a text, a template, an image or a document. Set type and the matching field.

> **Set up WhatsApp first.** Connect a WhatsApp number in the dashboard or with [Connect numbers](/docs/api-reference/whatsapp/importWhatsappNumbers). Included on every paid plan. [See plans](https://omnidim.io/pricing).

**POST** `/whatsapp/messages`

Send one message through `phone_number_id` to a customer, in a text,
a template, an image or a document. Set `type` and the matching field.

Outside the 24-hour window, only a template goes through. A text, image
or document send is refused with `409 window_closed`. A template send
works any time and can start a new conversation.

The send does not take the conversation over, so the agent keeps
answering. Set `take_over: true` to stop the agent and answer yourself.

`Idempotency-Key` is required. The same key with the same body replays
the first answer instead of sending again.

```yaml
operationId: sendWhatsappMessage
parameters:
  - in: header
    name: Idempotency-Key
    required: true
    schema:
      type: string
      maxLength: 255
    description: |
      A unique string you choose, up to 255 characters. Required on this
      route. The same key with the same body replays the first answer for
      24 hours instead of acting twice. A different body with the same key
      answers `409`.
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        required:
          - phone_number_id
          - to
          - type
        properties:
          phone_number_id:
            type: integer
            description: The number to send from.
          to:
            type: string
            description: The customer's number in international format, such as `+15551234567`.
          type:
            type: string
            enum:
              - text
              - template
              - image
              - document
          text:
            type: string
            maxLength: 4096
            description: Required when `type` is `text`.
          template:
            type: object
            description: Required when `type` is `template`.
            required:
              - id
            properties:
              id:
                type: integer
                description: An approved template on this number, from `listWhatsappTemplates`.
              params:
                type: object
                description: |
                  One value per field in the template's `fields`. Keys are
                  those field keys. String, number or null values.
                additionalProperties: true
          media:
            type: object
            description: Required when `type` is `image` or `document`.
            required:
              - media_id
            properties:
              media_id:
                type: string
                description: The `media_id` from `uploadWhatsappMedia`, uploaded within the last 24 hours.
              caption:
                type: string
                maxLength: 1024
          reply_to:
            type: integer
            description: A message id in the same conversation to quote. Not allowed with a template.
          take_over:
            type: boolean
            default: false
            description: Stop the agent answering this conversation and let a person run it.
      examples:
        text:
          summary: A text inside the window
          value:
            phone_number_id: 412
            to: '+15551234567'
            type: text
            text: Your appointment is confirmed for Friday at 10 am.
        template:
          summary: A template, any time
          value:
            phone_number_id: 412
            to: '+15551234567'
            type: template
            template:
              id: 3676
              params:
                '1': Rahul
                '2': Friday, 10 am
responses:
  '201':
    description: |
      The message was accepted. A provider failure that still created the
      row also returns `201`, with `status: "failed"` and a `failure`
      reason.
    content:
      application/json:
        schema:
          type: object
          properties:
            id:
              type: integer
            conversation_id:
              type: string
            wa_message_id:
              type: string
              nullable: true
              description: The message's id at WhatsApp, when there is one.
            sender:
              type: string
              enum:
                - customer
                - agent
                - person
            type:
              type: string
              description: |
                The message type: `text`, `image`, `video`, `audio`, `document`,
                `template`, `reaction`, `interactive`, `location`, `contacts`,
                `call` or `other`.
            text:
              type: string
              nullable: true
            caption:
              type: string
              nullable: true
            profile_name:
              type: string
              nullable: true
            sent_by_workflow:
              type: string
              nullable: true
              description: The workflow that sent it, when a workflow did.
            timestamp:
              type: string
            status:
              type: string
              enum:
                - pending
                - sent
                - delivered
                - read
                - received
                - failed
              description: |
                `received` is an inbound customer message. The rest are outbound, in
                order: `pending`, `sent`, `delivered`, `read`, or `failed`.
            reply_to:
              type: object
              nullable: true
              description: The message this one quotes, when it quotes one.
              properties:
                wa_message_id:
                  type: string
                preview:
                  type: string
                  nullable: true
                sender:
                  type: string
                  nullable: true
                  enum:
                    - customer
                    - agent
                    - person
            reactions:
              type: array
              items:
                type: object
                properties:
                  emoji:
                    type: string
                  sender:
                    type: string
                    enum:
                      - customer
                      - agent
                      - person
                  timestamp:
                    type: string
            media:
              type: object
              nullable: true
              description: Media details, when the message carries a file. It holds no download link.
              properties:
                mime:
                  type: string
                  nullable: true
                filename:
                  type: string
                  nullable: true
                size:
                  type: integer
                  nullable: true
                fetch_status:
                  type: string
                  enum:
                    - done
                    - pending
                    - failed
            voice:
              type: object
              nullable: true
              description: Voice note details, when the message is one.
              properties:
                duration_seconds:
                  type: number
                  nullable: true
                transcript:
                  type: string
                  nullable: true
            location:
              type: object
              nullable: true
              properties:
                lat:
                  type: number
                  nullable: true
                lng:
                  type: number
                  nullable: true
                name:
                  type: string
                  nullable: true
                address:
                  type: string
                  nullable: true
                map_url:
                  type: string
            contacts:
              type: array
              nullable: true
              items:
                type: object
                properties:
                  name:
                    type: string
                    nullable: true
                  phones:
                    type: array
                    items:
                      type: string
            interactive:
              type: object
              nullable: true
              properties:
                title:
                  type: string
                  nullable: true
            template:
              type: object
              nullable: true
              description: The template this message sent, when it is a template message.
              properties:
                id:
                  type: integer
                name:
                  type: string
            failure:
              type: object
              nullable: true
              description: Why a send failed, present only when `status` is `failed`.
              properties:
                code:
                  type: string
                  nullable: true
                  description: The provider's failure code, when there is one.
                reason:
                  type: string
                  nullable: true
                retryable:
                  type: boolean
                  description: |
                    Whether sending the same message again may work. `false` when
                    the message may already have been delivered.
            charged:
              type: object
              nullable: true
              description: What this send cost, when it has been charged.
              properties:
                amount:
                  type: number
                currency:
                  type: string
            timeline:
              type: array
              description: The delivery steps of this message, oldest first.
              items:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - received
                      - accepted
                      - sent
                      - delivered
                      - read
                      - failed
                  at:
                    type: string
                    nullable: true
  '400':
    description: |
      A bad body or a missing `Idempotency-Key`. `code` says which:
      `idempotency_key_missing`, `invalid_idempotency_key`,
      `invalid_json`, `invalid_parameter`, `missing_fields` (the response
      `fields` lists what a template still needs), `bad_reply_target` or
      `template_header_missing`.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
  '401':
    description: Missing or invalid API key.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: unauthorized
          error_description: Missing or invalid API key
          code: invalid_api_key
          request_id: req_6e2b9d4f1a38
  '402':
    description: |
      `insufficient_balance` (top up the wallet) or `monthly_limit_reached`
      (the plan's broadcast allowance is used up).
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: payment_required
          error_description: Insufficient balance to send a WhatsApp message. Top up your balance and try again.
          code: insufficient_balance
          request_id: req_1d4f7a2c9e63
  '403':
    description: |
      `feature_not_enabled` when your plan or role cannot use WhatsApp,
      `permission_denied` when the key's user cannot make changes, or
      `service_unavailable` when the account is on hold.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: forbidden
          error_description: You do not have permission to make changes to WhatsApp.
          code: permission_denied
          request_id: req_9f2c1a4b7d3e
  '404':
    description: '`number_not_found`, `template_not_found` or `media_not_found`.'
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
  '409':
    description: |
      `not_supported` (this number cannot send that type), `window_closed`
      (send a template instead), `opted_out`, or `idempotency_key_reused`
      (the same key with a different body).
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
  '500':
    description: Something went wrong on our side. Quote `ref` to support.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: server_error
          error_description: >-
            Something went wrong on our side. Please try again shortly, or contact support with
            reference 9a1f3e8b02c4.
          ref: 9a1f3e8b02c4
          code: server_error
          request_id: req_9a1f3e8b02c4
  '502':
    description: |
      A provider problem: `send_failed` (the message reached nothing),
      `template_sync_failed` (the template list is unchanged),
      `upstream_error` (a calling change), as `502`.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: bad_gateway
          error_description: The message could not be handed to WhatsApp. Nothing was sent or charged.
          code: send_failed
          request_id: req_3a9c6e1d8b52
  '503':
    description: |
      `channel_down`: WhatsApp is not connected for this number, nothing was
      sent or charged. `temporarily_unavailable`: template sync is briefly
      unavailable. Both carry `Retry-After`.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: service_unavailable
          error_description: WhatsApp is not connected for this number right now. Nothing was sent or charged.
          code: channel_down
          request_id: req_5c2f8a4d7e19
```

**curl**

```bash
curl -X POST "https://backend.omnidim.io/api/v1/whatsapp/messages" \
  -H "Authorization: Bearer $OMNIDIM_API_KEY"
```