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

# Enviar mensajes

> Contrato PIV1 de Chat: from, to, type y payload. Texto, imagen, audio y archivo.

<Tip>
  El envío de Chat usa el **mismo contrato** que los otros canales: `from`, `to`, `type` y `payload`. La diferencia: `to` es el **id de la conversación**, no un teléfono.
</Tip>

## Endpoint

```http theme={null}
POST /v1/chat/messages
Content-Type: application/json
Authorization: Bearer sk_live_xxxxx
```

Alcance: **`chat:messages:send`**. Cada envío consume **1 crédito** (`CHAT_MESSAGE`).

El path `POST /v1/chat/conversations/{id}/messages` sigue válido: el id de la conversación va en la URL y el cuerpo es el mismo (`type` + `payload`), sin `to`.

## Campos

| Campo                  | Obligatorio         | Qué es                                                        |
| ---------------------- | ------------------- | ------------------------------------------------------------- |
| `from`                 | No                  | Id del Chat App. Si viene, debe coincidir con la conversación |
| `to`                   | Sí en este endpoint | Id de la conversación: string o array                         |
| `type`                 | Sí                  | `text`, `image`, `audio` o `file`                             |
| `payload.message`      | Sí si `type=text`   | Texto. En media, es la leyenda (`caption` también vale)       |
| `payload.mediaUrl`     | Sí si no es texto   | URL **HTTPS** de la media                                     |
| `senderExternalUserId` | Sí con API Key      | Quién habla: `externalUserId` de un `USER` o `AGENT`          |
| `clientMessageId`      | No                  | Idempotencia: el mismo id devuelve el mismo mensaje           |
| `replyToId`            | No                  | Id del mensaje citado                                         |

Con **JWT del miembro**, no envíes `senderExternalUserId`: el remitente es el token.

La respuesta usa `type` en mayúsculas (`TEXT`, `IMAGE`, `AUDIO`, `FILE`).

***

## Texto

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_xxxxx"],
  "type": "text",
  "payload": {
    "message": "Hola Alice, tu pedido salió a entrega."
  },
  "senderExternalUserId": "agent:clxxuser...",
  "clientMessageId": "msg_text_001"
}
```

**Respuesta (200)**

```json theme={null}
{
  "success": true,
  "idempotent": false,
  "data": {
    "id": "clxxmsg...",
    "conversationId": "cnv_xxxxx",
    "type": "TEXT",
    "body": "Hola Alice, tu pedido salió a entrega.",
    "mediaUrl": null,
    "createdAt": "2026-09-06T03:10:00.000Z"
  }
}
```

***

## Imagen

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_xxxxx"],
  "type": "image",
  "payload": {
    "mediaUrl": "https://cdn.tusitio.com/comprobante.png",
    "message": "Comprobante de entrega"
  },
  "senderExternalUserId": "user_alice"
}
```

`payload.message` (o `payload.caption`) es la leyenda. Sin `payload.mediaUrl` → **400**.

***

## Audio

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_xxxxx"],
  "type": "audio",
  "payload": {
    "mediaUrl": "https://cdn.tusitio.com/nota.ogg"
  },
  "senderExternalUserId": "user_bob"
}
```

***

## Archivo

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_xxxxx"],
  "type": "file",
  "payload": {
    "mediaUrl": "https://cdn.tusitio.com/contrato.pdf",
    "message": "Contrato firmado"
  },
  "senderExternalUserId": "agent:clxxuser..."
}
```

***

## Responder a un mensaje

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_xxxxx"],
  "type": "text",
  "payload": { "message": "De acuerdo, gracias" },
  "senderExternalUserId": "agent:clxxuser...",
  "replyToId": "clxxmsg..."
}
```

`replyToId` también puede ir en `payload.replyToId`.

***

## JWT del miembro

La app autenticada con el JWT **no** envía API Key.

```http theme={null}
POST /v1/chat/messages
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
```

```json theme={null}
{
  "to": ["cnv_xxxxx"],
  "type": "text",
  "payload": { "message": "Ya llegué" }
}
```

***

## Varias conversaciones en el mismo pedido

`to` acepta lista. Cada id es una conversación del mismo workspace.

```json theme={null}
{
  "from": "clxxapp...",
  "to": ["cnv_aaaa", "cnv_bbbb"],
  "type": "text",
  "payload": { "message": "Mantenimiento a las 22h" },
  "senderExternalUserId": "agent:clxxuser..."
}
```

Una conversación → `data` es el objeto del mensaje. Varias → `data` es un array.

***

## WebSocket (app del usuario)

El socket **no** usa el body PIV1. Después del `auth`:

```json theme={null}
{ "type": "message.send", "conversationId": "cnv_xxxxx", "body": "Hola", "clientMessageId": "msg_ws_1" }
```

Media en el socket: `messageType` (`image` | `audio` | `file`) y `mediaUrl` HTTPS, o `payload.type` / `payload.mediaUrl`. El `type` del frame sigue siendo `message.send`.

REST (backend) → `POST /v1/chat/messages`. Tiempo real en la app → WebSocket.

Tipo fuera del techo de la app o del interruptor de la sala → **403** `CHAT_CAPABILITY_DISABLED`. DIRECT con bloqueo entre usuarios → **403** `CHAT_USER_BLOCKED`. Detalles: [Capacidades y bloqueo](/es/chat-api/como-funciona/capacidades-e-bloqueio).

En el WebSocket, `message.send` también acepta `type` (`text` | `image` | `audio` | `file`) y `mediaUrl` (HTTPS). `recording.start` solo vale si la sala tiene `audio` activo.

## Próximos pasos

* [Usuario y agente](/es/chat-api/como-funciona/usuario-e-agente): quién aparece como remitente
* [Eventos de webhooks](/es/chat-api/como-funciona/eventos-do-webhooks)
* [Alcances de la API Key](/es/chat-api/como-funciona/escopos-da-api-key)
