# Avox API Message Types

> Every message type the Avox API sends - text, templates, media, location, contacts, interactive, reactions - with field limits and full examples.

Source: https://docs.theavoxai.com/avox-api/messages

Every message goes to the same endpoint, `POST https://api.avox.com/api/messages/send`. The body
follows WhatsApp's own Cloud API message format: `to`, `type`, and one object named after the
type. This page shows every type the API accepts, with the limits Avox checks before anything is
sent.

| `type` | Use it for | Needs an open 24-hour window? |
| --- | --- | --- |
| [`text`](#text) | Plain text, optionally with a link preview | Yes |
| [`template`](#template) | An approved template - the only way to start a conversation | No |
| [`image`](#image), [`video`](#video), [`audio`](#audio), [`document`](#document), [`sticker`](#sticker) | Media by public link or media id | Yes |
| [`location`](#location) | A map pin | Yes |
| [`contacts`](#contacts) | One or more contact cards | Yes |
| [`interactive`](#interactive) | Reply buttons, lists, link buttons, Flows | Yes |
| [`reaction`](#reaction) | An emoji reaction to a message | Yes |

The 24-hour rule is WhatsApp's: outside it, only a template goes through, and anything else
returns [`outside_messaging_window`](https://docs.theavoxai.com/api/errors#outside_messaging_window). See
[Messaging window](https://docs.theavoxai.com/whatsapp/messaging-window).

## Setup for the examples

The curl examples are complete on their own. The Node.js and Python examples on this page call a
small `send` helper, so each example only shows the message itself:

**curl**

```bash
export AVOX_API_KEY="avx_live_your_key_here"
```

**Node.js**

```js
// send.js - Node.js 18 or later (built-in fetch)
import { randomUUID } from "node:crypto";

export async function send(message, idempotencyKey = randomUUID()) {
  const res = await fetch("https://api.avox.com/api/messages/send", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.AVOX_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey,
    },
    body: JSON.stringify(message),
  });
  const data = await res.json();
  if (!data.ok) {
    throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
  }
  return data;
}
```

**Python**

```python
# send.py
import os
import uuid

import requests


def send(message, idempotency_key=None):
    res = requests.post(
        "https://api.avox.com/api/messages/send",
        headers={
            "Authorization": f"Bearer {os.environ['AVOX_API_KEY']}",
            "Idempotency-Key": idempotency_key or str(uuid.uuid4()),
        },
        json=message,
        timeout=30,
    )
    data = res.json()
    if not data["ok"]:
        raise RuntimeError(f"{res.status_code} {data['error']['code']}: {data['error']['message']}")
    return data
```

Every successful send returns the same shape, whatever the type:

```json
{
  "ok": true,
  "request_id": "req_4f1c9a0b7e2d36a8c5b1f0e9",
  "message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBI5",
  "result": {
    "messaging_product": "whatsapp",
    "contacts": [{ "input": "15551234567", "wa_id": "15551234567" }],
    "messages": [{ "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBI5" }]
  }
}
```

`result` is WhatsApp's own reply, passed back unchanged. `message_id` is the same id as
`result.messages[0].id`, lifted to the top level for convenience.

A [test key](https://docs.theavoxai.com/avox-api/testing#1-test-mode) accepts exactly the same bodies and runs the same checks
on them, so every example here works in test mode too - the answer adds `"test": true` and a
`message_id` starting with `wamid.TEST`, and nothing is sent.

## Fields every message can use

| Field | Required | Rules |
| --- | --- | --- |
| `to` | Yes | The recipient's full international number with country code: 7 to 15 digits, not starting with `0`. Spaces, dashes, dots, brackets and one leading `+` are allowed and removed. `+44 7700 900123` and `447700900123` are the same. |
| `type` | Yes | One of the eleven types on this page. |
| `recipient_type` | No | Only `"individual"`. |
| `context.message_id` | No | Reply to a specific message (quotes it in the chat). 1 to 256 characters - use the `id` of a message you received. |
| `biz_opaque_callback_data` | No | Your own reference, up to 512 characters. WhatsApp returns it on this message's status updates. See [Delivery statuses and replies](https://docs.theavoxai.com/avox-api/webhooks). |

Any other top-level field (for example `messaging_product`) is dropped. Avox adds
`messaging_product: "whatsapp"` itself. The whole body must be under 64 KB.

## Text

`text.body` is required: 1 to 4096 characters. Set `text.preview_url` to `true` to show a preview
of the first link in the message.

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "text",
    "text": {
      "body": "Your order A-1042 has shipped: https://example.com/track/A-1042",
      "preview_url": true
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

const result = await send({
  to: "15551234567",
  type: "text",
  text: {
    body: "Your order A-1042 has shipped: https://example.com/track/A-1042",
    preview_url: true,
  },
});
console.log(result.message_id);
```

**Python**

```python
from send import send

result = send({
    "to": "15551234567",
    "type": "text",
    "text": {
        "body": "Your order A-1042 has shipped: https://example.com/track/A-1042",
        "preview_url": True,
    },
})
print(result["message_id"])
```

### Replying to a message

Add `context.message_id` with the id of the customer's message to quote it:

```json
{
  "to": "15551234567",
  "type": "text",
  "context": { "message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQTdC" },
  "text": { "body": "Yes - your table for 4 is booked for 8pm." }
}
```

## Template

Templates are the only messages you can send outside the 24-hour window, and the only way to start
a conversation. The template must be approved for the WhatsApp Business Account behind your key's
number. Find the exact name and language on the [Templates](https://docs.theavoxai.com/templates) page.

| Field | Rules |
| --- | --- |
| `template.name` | Required. Lowercase letters, numbers and underscores, up to 512 characters. |
| `template.language.code` | Required. A code like `en` or `en_US`. It must match a language the template was approved in - `en` and `en_US` are different. |
| `template.language.policy` | Optional. Only `"deterministic"`. |
| `template.components` | Optional. At most 20 components. Each has a `type` (like `header`, `body`, `button`), and buttons also take `sub_type` and `index` (0 to 20). |
| `components[].parameters` | At most 100 per component. Each has a `type` (like `text`, `image`, `currency`, `date_time`, `payload`); a `text` value can be up to 1024 characters. |

Parameters fill the template's placeholders in order (`{{1}}`, `{{2}}`, ...). For a template built
with named placeholders, add `parameter_name` to each parameter, as WhatsApp's format describes.

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "template",
    "template": {
      "name": "order_shipped",
      "language": { "code": "en_US" },
      "components": [
        {
          "type": "header",
          "parameters": [
            { "type": "image", "image": { "link": "https://example.com/parcel.jpg" } }
          ]
        },
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Maya" },
            { "type": "text", "text": "A-1042" }
          ]
        },
        {
          "type": "button",
          "sub_type": "url",
          "index": "0",
          "parameters": [{ "type": "text", "text": "A-1042" }]
        }
      ]
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "template",
  template: {
    name: "order_shipped",
    language: { code: "en_US" },
    components: [
      {
        type: "header",
        parameters: [{ type: "image", image: { link: "https://example.com/parcel.jpg" } }],
      },
      {
        type: "body",
        parameters: [
          { type: "text", text: "Maya" },
          { type: "text", text: "A-1042" },
        ],
      },
      {
        type: "button",
        sub_type: "url",
        index: "0",
        parameters: [{ type: "text", text: "A-1042" }],
      },
    ],
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "template",
    "template": {
        "name": "order_shipped",
        "language": {"code": "en_US"},
        "components": [
            {
                "type": "header",
                "parameters": [{"type": "image", "image": {"link": "https://example.com/parcel.jpg"}}],
            },
            {
                "type": "body",
                "parameters": [
                    {"type": "text", "text": "Maya"},
                    {"type": "text", "text": "A-1042"},
                ],
            },
            {
                "type": "button",
                "sub_type": "url",
                "index": "0",
                "parameters": [{"type": "text", "text": "A-1042"}],
            },
        ],
    },
})
```

A template with no placeholders needs only `name` and `language`:

```json
{
  "to": "15551234567",
  "type": "template",
  "template": { "name": "hello_world", "language": { "code": "en_US" } }
}
```

For a quick reply button that should send your server a known value, use a `payload` parameter:

```json
{
  "type": "button",
  "sub_type": "quick_reply",
  "index": 0,
  "parameters": [{ "type": "payload", "payload": "CONFIRM_A-1042" }]
}
```

WhatsApp's template errors come back as their own codes:
[`template_not_found`](https://docs.theavoxai.com/api/errors#template_not_found) (wrong name or language),
[`template_unavailable`](https://docs.theavoxai.com/api/errors#template_unavailable) (paused or disabled), and
[`template_params_invalid`](https://docs.theavoxai.com/api/errors#template_params_invalid) (parameters don't fit). See
[Template approval](https://docs.theavoxai.com/templates/approval) and [Template categories](https://docs.theavoxai.com/whatsapp/template-categories).

## Media messages

`image`, `video`, `audio`, `document` and `sticker` all point at the file one of two ways - give
**exactly one**:

- `link` - a public `https://` URL, under 2048 characters. WhatsApp downloads the file from there,
  so it must be reachable without signing in.
- `id` - a WhatsApp media id that already exists for this number (letters, numbers, `-` and `_`,
  up to 128 characters).

Sending both, or neither, returns [`invalid_payload`](https://docs.theavoxai.com/api/errors#invalid_payload) with "Give
exactly one of id or link". If WhatsApp can't download a `link`, the send is refused with
[`meta_rejected`](https://docs.theavoxai.com/api/errors#meta_rejected).

| Type | Extra fields |
| --- | --- |
| `image` | `caption` up to 1024 characters |
| `video` | `caption` up to 1024 characters |
| `document` | `caption` up to 1024 characters, `filename` up to 240 characters |
| `audio` | None |
| `sticker` | None |

WhatsApp also sets its own rules for file types and sizes per media type, and checks them when it
downloads the file.

### Image

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "image",
    "image": {
      "link": "https://example.com/receipts/A-1042.png",
      "caption": "Your receipt for order A-1042"
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "image",
  image: {
    link: "https://example.com/receipts/A-1042.png",
    caption: "Your receipt for order A-1042",
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "image",
    "image": {
        "link": "https://example.com/receipts/A-1042.png",
        "caption": "Your receipt for order A-1042",
    },
})
```

### Video

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "video",
    "video": {
      "link": "https://example.com/videos/setup-guide.mp4",
      "caption": "A 60-second setup guide"
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "video",
  video: { link: "https://example.com/videos/setup-guide.mp4", caption: "A 60-second setup guide" },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "video",
    "video": {"link": "https://example.com/videos/setup-guide.mp4", "caption": "A 60-second setup guide"},
})
```

### Audio

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "audio",
    "audio": { "link": "https://example.com/audio/welcome.mp3" }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "audio",
  audio: { link: "https://example.com/audio/welcome.mp3" },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "audio",
    "audio": {"link": "https://example.com/audio/welcome.mp3"},
})
```

### Document

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "document",
    "document": {
      "link": "https://example.com/invoices/INV-2041.pdf",
      "filename": "INV-2041.pdf",
      "caption": "Your invoice for September"
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "document",
  document: {
    link: "https://example.com/invoices/INV-2041.pdf",
    filename: "INV-2041.pdf",
    caption: "Your invoice for September",
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "document",
    "document": {
        "link": "https://example.com/invoices/INV-2041.pdf",
        "filename": "INV-2041.pdf",
        "caption": "Your invoice for September",
    },
})
```

### Sticker

WhatsApp stickers are WebP images.

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "sticker",
    "sticker": { "link": "https://example.com/stickers/thanks.webp" }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "sticker",
  sticker: { link: "https://example.com/stickers/thanks.webp" },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "sticker",
    "sticker": {"link": "https://example.com/stickers/thanks.webp"},
})
```

## Location

| Field | Rules |
| --- | --- |
| `location.latitude` | Required number, -90 to 90. |
| `location.longitude` | Required number, -180 to 180. |
| `location.name` | Optional, up to 1000 characters. |
| `location.address` | Optional, up to 1000 characters. |

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "location",
    "location": {
      "latitude": 51.5072,
      "longitude": -0.1276,
      "name": "Acme Store - Central",
      "address": "1 Example Street, London"
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "location",
  location: {
    latitude: 51.5072,
    longitude: -0.1276,
    name: "Acme Store - Central",
    address: "1 Example Street, London",
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "location",
    "location": {
        "latitude": 51.5072,
        "longitude": -0.1276,
        "name": "Acme Store - Central",
        "address": "1 Example Street, London",
    },
})
```

## Contacts

`contacts` is an array of 1 to 20 contact cards. Each card needs `name.formatted_name` (1 to 1000
characters). Every other field WhatsApp's contact format supports (`phones`, `emails`, `org`,
`urls`, `addresses`, the rest of `name`) is passed through.

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "contacts",
    "contacts": [
      {
        "name": { "formatted_name": "Acme Support", "first_name": "Acme" },
        "phones": [{ "phone": "+15550001111", "type": "WORK" }],
        "emails": [{ "email": "help@example.com", "type": "WORK" }]
      }
    ]
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "contacts",
  contacts: [
    {
      name: { formatted_name: "Acme Support", first_name: "Acme" },
      phones: [{ phone: "+15550001111", type: "WORK" }],
      emails: [{ email: "help@example.com", type: "WORK" }],
    },
  ],
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "contacts",
    "contacts": [
        {
            "name": {"formatted_name": "Acme Support", "first_name": "Acme"},
            "phones": [{"phone": "+15550001111", "type": "WORK"}],
            "emails": [{"email": "help@example.com", "type": "WORK"}],
        }
    ],
})
```

## Interactive

Interactive messages carry buttons, lists, link buttons or a Flow. Avox checks the shared parts;
WhatsApp checks the rest (such as how many buttons or rows each kind allows) and refuses anything
outside its rules with [`meta_rejected`](https://docs.theavoxai.com/api/errors#meta_rejected).

| Field | Rules |
| --- | --- |
| `interactive.type` | Required. Lowercase letters and underscores, like `button`, `list`, `cta_url` or `flow`. |
| `interactive.header` | Optional. Needs a `type` (like `text`, `image`, `video`, `document`). |
| `interactive.body.text` | Up to 1024 characters. |
| `interactive.footer.text` | Up to 60 characters. |
| `interactive.action` | An object in WhatsApp's format for that kind - passed through as sent. |

### Reply buttons

When the customer taps a button, its `id` comes back in the reply. With message forwarding on, your
server gets it as `data.interactive.id` in a `message.received` event. See
[Interactive replies](https://docs.theavoxai.com/avox-api/webhooks#whats-sent).

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "interactive",
    "interactive": {
      "type": "button",
      "body": { "text": "Is Friday at 3pm still good for delivery?" },
      "footer": { "text": "Acme Deliveries" },
      "action": {
        "buttons": [
          { "type": "reply", "reply": { "id": "slot_keep", "title": "Yes, Friday" } },
          { "type": "reply", "reply": { "id": "slot_change", "title": "Pick another day" } }
        ]
      }
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "interactive",
  interactive: {
    type: "button",
    body: { text: "Is Friday at 3pm still good for delivery?" },
    footer: { text: "Acme Deliveries" },
    action: {
      buttons: [
        { type: "reply", reply: { id: "slot_keep", title: "Yes, Friday" } },
        { type: "reply", reply: { id: "slot_change", title: "Pick another day" } },
      ],
    },
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "interactive",
    "interactive": {
        "type": "button",
        "body": {"text": "Is Friday at 3pm still good for delivery?"},
        "footer": {"text": "Acme Deliveries"},
        "action": {
            "buttons": [
                {"type": "reply", "reply": {"id": "slot_keep", "title": "Yes, Friday"}},
                {"type": "reply", "reply": {"id": "slot_change", "title": "Pick another day"}},
            ]
        },
    },
})
```

### List

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "interactive",
    "interactive": {
      "type": "list",
      "header": { "type": "text", "text": "Book a visit" },
      "body": { "text": "Choose a time that suits you." },
      "action": {
        "button": "See times",
        "sections": [
          {
            "title": "Tomorrow",
            "rows": [
              { "id": "t_0900", "title": "9:00", "description": "Morning" },
              { "id": "t_1400", "title": "14:00", "description": "Afternoon" }
            ]
          }
        ]
      }
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "interactive",
  interactive: {
    type: "list",
    header: { type: "text", text: "Book a visit" },
    body: { text: "Choose a time that suits you." },
    action: {
      button: "See times",
      sections: [
        {
          title: "Tomorrow",
          rows: [
            { id: "t_0900", title: "9:00", description: "Morning" },
            { id: "t_1400", title: "14:00", description: "Afternoon" },
          ],
        },
      ],
    },
  },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "interactive",
    "interactive": {
        "type": "list",
        "header": {"type": "text", "text": "Book a visit"},
        "body": {"text": "Choose a time that suits you."},
        "action": {
            "button": "See times",
            "sections": [
                {
                    "title": "Tomorrow",
                    "rows": [
                        {"id": "t_0900", "title": "9:00", "description": "Morning"},
                        {"id": "t_1400", "title": "14:00", "description": "Afternoon"},
                    ],
                }
            ],
        },
    },
})
```

### Link button

```json
{
  "to": "15551234567",
  "type": "interactive",
  "interactive": {
    "type": "cta_url",
    "body": { "text": "Your September statement is ready." },
    "action": {
      "name": "cta_url",
      "parameters": { "display_text": "View statement", "url": "https://example.com/statements/2026-09" }
    }
  }
}
```

### Flow

Opens a published WhatsApp Flow inside the chat. Get the Flow's id from the [Flows](https://docs.theavoxai.com/flows) page.

```json
{
  "to": "15551234567",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "body": { "text": "Tell us how your visit went - it takes a minute." },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_message_version": "3",
        "flow_id": "1234567890",
        "flow_cta": "Start survey",
        "flow_token": "survey-A-1042",
        "flow_action": "navigate",
        "flow_action_payload": { "screen": "WELCOME" }
      }
    }
  }
}
```

See [Flows on WhatsApp](https://docs.theavoxai.com/whatsapp/flows) for how Flows work on Meta's side.

## Reaction

| Field | Rules |
| --- | --- |
| `reaction.message_id` | Required, 1 to 256 characters - the id of the message you're reacting to. |
| `reaction.emoji` | Up to 16 characters. Send an empty string to remove your reaction. |

**curl**

```bash
curl -X POST https://api.avox.com/api/messages/send \
  -H "Authorization: Bearer $AVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "15551234567",
    "type": "reaction",
    "reaction": {
      "message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQTdC",
      "emoji": "👍"
    }
  }'
```

**Node.js**

```js
import { send } from "./send.js";

await send({
  to: "15551234567",
  type: "reaction",
  reaction: { message_id: "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQTdC", emoji: "\u{1F44D}" },
});
```

**Python**

```python
from send import send

send({
    "to": "15551234567",
    "type": "reaction",
    "reaction": {"message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQTdC", "emoji": "\U0001F44D"},
})
```

## When a body is refused

Avox checks the body in this order and stops at the first problem:

1. `to` must be a valid number - otherwise [`invalid_recipient`](https://docs.theavoxai.com/api/errors#invalid_recipient).
2. `type` must be present - otherwise [`invalid_payload`](https://docs.theavoxai.com/api/errors#invalid_payload) with
   `path` `type`.
3. `type` must be one of the eleven types - otherwise
   [`unsupported_message_type`](https://docs.theavoxai.com/api/errors#unsupported_message_type).
4. Every field must fit the rules above - otherwise
   [`invalid_payload`](https://docs.theavoxai.com/api/errors#invalid_payload).

`invalid_payload` lists up to 10 problems in `details`. Each has a `path` (the field, with dots
between levels) and a `message`. Your submitted values are never echoed back:

```json
{
  "ok": false,
  "error": {
    "code": "invalid_payload",
    "message": "The request body isn't valid. The details list says which fields to fix.",
    "docs_url": "https://docs.avox.com/api/errors#invalid_payload",
    "request_id": "req_0c7d1e4b9a2f63815e7d0a42",
    "details": [
      { "path": "text.body", "message": "Text messages must be at most 4096 characters" }
    ]
  }
}
```
