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

# POST /v1/messages/raw

> Escape hatch: forward an unmodified Meta Graph API body. Use only when no typed endpoint fits.

Forwards `body` as-is to Meta's `/{phone_number_id}/messages`. Keebai adds the access token and the routing, and validates nothing inside `body`.

<Warning>
  This is the escape hatch, not a shortcut. Every other endpoint on this page validates your payload, maps errors, and stays stable across Meta API versions. `raw` does none of that: if Meta changes its schema, your integration breaks with no warning from us.
</Warning>

Reach for it only when Meta ships a message type Keebai has not exposed yet — and tell us, so we can add a typed endpoint.

## Endpoint

```
POST https://api.keebai.com/v1/messages/raw
```

## Required scope

`messages:send`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |
| `Content-Type`  | Yes      | `application/json`       |

## Body

| Field             | Type     | Required | Description                                                                                                        |
| ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `channel_id`      | `string` | No       | Keebai channel id of the WhatsApp channel. Only needed when the project has more than one active WhatsApp channel. |
| `phone_number_id` | `string` | No       | Alias for `channel_id`: the WhatsApp Business `phone_number_id` of the sending number.                             |
| `body`            | `object` | Yes      | Raw Meta Graph API messages body. Sent unmodified.                                                                 |
| `meta_data`       | `object` | No       | Free-form metadata persisted with the message. Not forwarded to Meta.                                              |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.keebai.com/v1/messages/raw \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "phone_number_id": "109876543210987",
      "body": {
        "messaging_product": "whatsapp",
        "recipient_type": "individual",
        "to": "5491155555555",
        "type": "text",
        "text": { "preview_url": false, "body": "Enviado vía raw" }
      }
    }'
  ```

  ```js JavaScript theme={null}
  await fetch("https://api.keebai.com/v1/messages/raw", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      phone_number_id: "109876543210987",
      body: {
        messaging_product: "whatsapp",
        recipient_type: "individual",
        to: "5491155555555",
        type: "text",
        text: { preview_url: false, body: "Enviado vía raw" },
      },
    }),
  });
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      "https://api.keebai.com/v1/messages/raw",
      headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
      json={
          "phone_number_id": "109876543210987",
          "body": {
              "messaging_product": "whatsapp",
              "recipient_type": "individual",
              "to": "5491155555555",
              "type": "text",
              "text": {"preview_url": False, "body": "Enviado vía raw"},
          },
      },
      timeout=10,
  )
  resp.raise_for_status()
  ```
</CodeGroup>

## Response

### 202 Accepted

```json theme={null}
{
  "message_id": "wamid.HBgLNTQ5MTE1NTU1NTU1FQIAERgSN0EyRjc4...",
  "status": "sent",
  "sent_at": "2026-05-02T14:31:07.221Z"
}
```

### 400 Bad Request

`body` missing or not an object, or an unknown top-level property. **Nothing inside `body` is validated** — a malformed Meta payload surfaces as a `502` instead.

### 401 Unauthorized

Missing, invalid, revoked, or expired token.

### 403 Forbidden

The token does not have the `messages:send` scope.

### 502 Bad Gateway

Meta rejected the payload. The upstream error message is passed through, so read it carefully — it is your only debugging signal here.

## Operational notes

* **`to` inside `body` has no `+`.** Meta's raw format wants `5491155555555`; every typed Keebai endpoint wants `+5491155555555`. Mixing the two conventions is the most common mistake with this endpoint.
* **`messaging_product: "whatsapp"` is mandatory** in the raw body. Meta rejects the call without it.
* **You still get Keebai's persistence**: the message is stored against the conversation and reported through webhooks like any other, so `meta_data` still works for correlation.


## OpenAPI

````yaml POST /v1/messages/raw
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/messages/raw:
    post:
      tags:
        - messages
      summary: >-
        Escape hatch: forward a raw Meta Graph API body to
        /{phone_number_id}/messages
      description: WhatsApp Cloud API only.
      operationId: PublicMessagesController_sendRaw
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendRawDto'
      responses:
        '202':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SingleSendResponse'
      security:
        - PAT: []
components:
  schemas:
    SendRawDto:
      type: object
      properties:
        channel_id:
          type: string
          example: 665f1a2b3c4d5e6f70819234
          description: >-
            Keebai channel id. Optional when the tenant has exactly one active
            channel of the given type; required to disambiguate otherwise.
        phone_number_id:
          type: string
          example: '100000000000001'
          description: >-
            WhatsApp-only alias for channel_id: the Meta phone_number_id of the
            sending number. Ignored on other channels.
        body:
          type: object
          description: >-
            Raw Meta Graph API messages body. Sent as-is to
            /{phone_number_id}/messages. Use only when POST /v1/messages has no
            matching type.
        meta_data:
          type: object
          description: >-
            Free-form metadata persisted with the message. Returned on webhooks
            for correlation.
      required:
        - body
    SingleSendResponse:
      type: object
      properties:
        message_id:
          type: string
          description: Meta message_id (wamid.*) of the sent WhatsApp message.
        status:
          type: string
          example: sent
        sent_at:
          type: string
          description: ISO-8601 timestamp of the dispatch.
      required:
        - message_id
        - status
        - sent_at
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````