import: contenido inicial de la skill whatsapp
This commit is contained in:
304
endpoints.md
Normal file
304
endpoints.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# whatsapp-bridge — referencia rápida de endpoints
|
||||
|
||||
Base URL: `https://whatsapp.nucleoriofrio.com`
|
||||
Auth: `Authorization: Bearer $WA_BRIDGE_TOKEN` (excepto `/healthz`)
|
||||
|
||||
## Diagnóstico
|
||||
|
||||
| Método | Ruta | Respuesta |
|
||||
|---|---|---|
|
||||
| GET | `/healthz` | `{ ok: true, ts }` |
|
||||
| GET | `/api/auth/status` | `{ status, hasSession, user, pairingCode, lastError }` |
|
||||
|
||||
`status` ∈ `idle | connecting | open | closed | needs-pair`.
|
||||
|
||||
## Auth / pairing
|
||||
|
||||
| Método | Ruta | Body / Headers |
|
||||
|---|---|---|
|
||||
| POST | `/api/auth/pair` | — → `{ code, hint }` |
|
||||
| POST | `/api/auth/logout` | Header `X-Confirm-Wipe: yes` (destructivo) |
|
||||
|
||||
## Chats
|
||||
|
||||
```
|
||||
GET /api/chats
|
||||
?type=group|individual|all (default all)
|
||||
&q=<texto> (busca en name + jid, ILIKE)
|
||||
&limit=<1..500> (default 100)
|
||||
&offset=<int> (default 0)
|
||||
```
|
||||
|
||||
Respuesta:
|
||||
```json
|
||||
{
|
||||
"count": N,
|
||||
"chats": [
|
||||
{ "jid": "...@g.us", "isGroup": true, "name": "...", "lastMessageAt": "ISO", "participants": 42 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Grupo específico
|
||||
|
||||
```
|
||||
GET /api/groups/:jid (URL-encode el jid, termina en @g.us)
|
||||
```
|
||||
Respuesta: `{ jid, stored: {…}, live: GroupMetadata | null }`.
|
||||
|
||||
```
|
||||
GET /api/groups/:jid/messages
|
||||
?since=<ISO> (timestamp >=)
|
||||
&until=<ISO> (timestamp <=)
|
||||
&q=<texto> (ILIKE en content)
|
||||
&sender=<jid> (filtra por participant)
|
||||
&order=asc|desc (default desc)
|
||||
&limit=<1..1000> (default 100)
|
||||
&offset=<int>
|
||||
```
|
||||
|
||||
Respuesta:
|
||||
```json
|
||||
{
|
||||
"jid": "...@g.us",
|
||||
"count": N,
|
||||
"filters": { ... },
|
||||
"messages": [
|
||||
{
|
||||
"msgId": "...",
|
||||
"fromMe": false,
|
||||
"senderJid": "...@s.whatsapp.net",
|
||||
"pushName": "Don Fulano",
|
||||
"messageType": "extendedTextMessage",
|
||||
"content": "texto extraído",
|
||||
"contextInfo": { "quotedMsgId": "...", "mentionedJid": ["..."] } | null,
|
||||
"timestamp": "ISO",
|
||||
"media": null | {
|
||||
"type": "image"|"video"|"audio"|"document"|"sticker",
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 12345,
|
||||
"fileName": "... (solo document)",
|
||||
"caption": "... (image/video/document)",
|
||||
"durationSec": 30 (video/audio),
|
||||
"ptt": true (solo audio = voice note),
|
||||
"width": 800 (image/video/sticker),
|
||||
"height": 600,
|
||||
"pages": 5 (solo document),
|
||||
"isAnimated": true (solo sticker),
|
||||
"downloadUrl": "/api/messages/media/<enc-chatJid>/<msgId>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
El campo `media` aparece en cada mensaje que contiene archivo; `downloadUrl` es la ruta relativa al endpoint que descarga + desencripta.
|
||||
|
||||
## Mensajes individuales
|
||||
|
||||
```
|
||||
GET /api/messages/individual/:jid (termina en @s.whatsapp.net)
|
||||
?since=&until=&q=&order=&limit=&offset=
|
||||
```
|
||||
|
||||
## Búsqueda cross-chat
|
||||
|
||||
```
|
||||
GET /api/messages/search
|
||||
?q=<texto> (requerido, min 1 char, ILIKE)
|
||||
&since=<ISO>
|
||||
&until=<ISO>
|
||||
&chatType=group|individual|all (default all)
|
||||
&limit=<1..500> (default 100)
|
||||
```
|
||||
|
||||
Respuesta:
|
||||
```json
|
||||
{
|
||||
"query": "quintal",
|
||||
"count": N,
|
||||
"matches": [
|
||||
{ "chatJid": "...", "msgId": "...", "senderJid": "...", "pushName": "...", "isGroup": true, "content": "...", "timestamp": "ISO" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Envío de texto (gated por ENABLE_SEND)
|
||||
|
||||
```
|
||||
POST /api/messages/send
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"jid": "...@g.us" | "...@s.whatsapp.net" | "...@lid",
|
||||
"text": "...",
|
||||
"quotedChatJid": "...", // opcional; default = mismo jid destino
|
||||
"quotedMsgId": "...", // opcional; responder a mensaje previo
|
||||
"mentions": ["jid1", ...] // opcional; notifica a esos JIDs
|
||||
}
|
||||
```
|
||||
|
||||
Respuesta: `{ ok: true, msgId: "..." }` o 403 si ENABLE_SEND=false.
|
||||
|
||||
## Reenvío (forward)
|
||||
|
||||
```
|
||||
POST /api/messages/forward
|
||||
|
||||
{
|
||||
"sourceChatJid": "...", // chat donde está el mensaje original
|
||||
"sourceMsgId": "...", // id del mensaje
|
||||
"destJid": "..." // destino
|
||||
}
|
||||
```
|
||||
|
||||
Requiere que el mensaje esté en la DB del bridge (escuchado mientras el socket estaba vivo).
|
||||
|
||||
## Reacción (emoji)
|
||||
|
||||
```
|
||||
POST /api/messages/react
|
||||
|
||||
{
|
||||
"chatJid": "...",
|
||||
"msgId": "...",
|
||||
"emoji": "👍" // vacío "" = quitar reacción
|
||||
}
|
||||
```
|
||||
|
||||
## Envío de multimedia
|
||||
|
||||
```
|
||||
POST /api/messages/send-media
|
||||
|
||||
{
|
||||
"jid": "...",
|
||||
"url": "https://...", // URL pública del archivo
|
||||
"type": "image" | "video" | "audio" | "document",
|
||||
"caption": "...", // opcional (solo image/video/document)
|
||||
"fileName": "...", // opcional (solo document)
|
||||
"mimetype": "...", // opcional (override del content-type)
|
||||
"ptt": false, // solo audio: true = voice note
|
||||
"quotedChatJid": "...", // opcional
|
||||
"quotedMsgId": "..." // opcional
|
||||
}
|
||||
```
|
||||
|
||||
Límites y guards:
|
||||
- **URL debe ser HTTP/HTTPS pública** — localhost, 127.0.0.1, 10.x, 192.168.x, 172.16-31.x son bloqueadas (SSRF protection).
|
||||
- **Tamaño máximo ~100 MB** (aunque WhatsApp limita más bajo según tipo).
|
||||
- Timeout del fetch: 60s.
|
||||
|
||||
## Envío de ubicación
|
||||
|
||||
```
|
||||
POST /api/messages/send-location
|
||||
|
||||
{
|
||||
"jid": "...",
|
||||
"latitude": 14.123,
|
||||
"longitude": -88.456,
|
||||
"name": "...", // opcional (ej: "Beneficio Rio Frio")
|
||||
"address": "...", // opcional (ej: "Santa Bárbara, Honduras")
|
||||
"quotedChatJid": "...",
|
||||
"quotedMsgId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## Envío de encuesta (poll)
|
||||
|
||||
```
|
||||
POST /api/messages/send-poll
|
||||
|
||||
{
|
||||
"jid": "...",
|
||||
"name": "Pregunta",
|
||||
"values": ["Opción A", "Opción B", "Opción C"], // 2-12 opciones
|
||||
"selectableCount": 1, // 1=single (default), 0=multi-choice, N=hasta N
|
||||
"quotedChatJid": "...",
|
||||
"quotedMsgId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## Envío de contacto(s) (vCard)
|
||||
|
||||
```
|
||||
POST /api/messages/send-contact
|
||||
|
||||
{
|
||||
"jid": "...",
|
||||
"contacts": [
|
||||
{
|
||||
"displayName": "Don Fulano",
|
||||
"firstName": "Fulano", // opcional
|
||||
"lastName": "Pérez", // opcional
|
||||
"phone": "50499999999", // opcional, E.164 sin + → vinculable a WA
|
||||
"phoneDisplay": "+504 9999-9999", // opcional, default = "+" + phone
|
||||
"email": "...", // opcional
|
||||
"organization": "...", // opcional
|
||||
"title": "...", // opcional
|
||||
"vcard": "BEGIN:VCARD..." // opcional, override total
|
||||
}
|
||||
],
|
||||
"quotedChatJid": "...",
|
||||
"quotedMsgId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
Hasta 20 contactos por mensaje. Si `phone` está en formato E.164 (sin `+`), WhatsApp lo detecta y permite iniciar chat con un tap.
|
||||
|
||||
## Descarga de media (desencriptado server-side)
|
||||
|
||||
Los mensajes con media incluyen `media.downloadUrl` en las respuestas. Ese path
|
||||
apunta a un endpoint que, al llamarlo con el bearer, descarga el archivo del
|
||||
CDN de WhatsApp, lo desencripta con la `mediaKey` del mensaje, y devuelve el
|
||||
binario listo para usar.
|
||||
|
||||
```
|
||||
GET /api/messages/media/:chatJid/:msgId[?disposition=inline|attachment]
|
||||
```
|
||||
|
||||
Headers de respuesta:
|
||||
- `Content-Type`: MIME correcto (image/jpeg, video/mp4, etc.)
|
||||
- `Content-Length`: tamaño en bytes
|
||||
- `Content-Disposition`: `inline` (default) o `attachment`
|
||||
- `X-Wa-Media-Type`: `image|video|audio|document|sticker`
|
||||
|
||||
Solo metadata (sin bajar el binario):
|
||||
|
||||
```
|
||||
GET /api/messages/media-info/:chatJid/:msgId
|
||||
→ { chatJid, msgId, media: { type, mimetype, size, fileName, caption, ..., downloadUrl } }
|
||||
```
|
||||
|
||||
Errores:
|
||||
- 404 si el mensaje no existe o no tiene media
|
||||
- 503 si el socket de WhatsApp está caído
|
||||
- 500 si el CDN de WA ya no tiene el archivo (muy viejo → no se puede recuperar)
|
||||
|
||||
## Eventos (no soportado todavía)
|
||||
|
||||
Baileys 6.17 no expone una API estable para crear event messages (los puede recibir/parsear pero no enviar via `sendMessage`). Sería necesario construir `proto.Message.IEventMessage` manualmente y usar `relayMessage` low-level — todavía no implementado en el bridge. Si lo necesitás urgente, abrir issue.
|
||||
|
||||
Errores típicos: 400 con `fetch failed: ...` si la URL no responde bien o es privada.
|
||||
|
||||
## Formatos de JID
|
||||
|
||||
| JID | Tipo | Ejemplo |
|
||||
|---|---|---|
|
||||
| `<phone>@s.whatsapp.net` | chat individual | `50499999999@s.whatsapp.net` |
|
||||
| `<phone>-<timestamp>@g.us` | grupo | `50499999999-1614182736@g.us` |
|
||||
| `status@broadcast` | estados | (ignorado por el bridge) |
|
||||
|
||||
URL-encoding en path params: `@` → `%40`, `.` se deja tal cual.
|
||||
|
||||
## Códigos de error
|
||||
|
||||
| Status | Significado |
|
||||
|---|---|
|
||||
| 400 | parámetros inválidos (ver body con `.error`) |
|
||||
| 401 | bearer ausente o inválido |
|
||||
| 403 | endpoint gated (ej send cuando ENABLE_SEND=false) |
|
||||
| 404 | chat/grupo no existe en storage |
|
||||
| 500 | error interno (revisar logs del contenedor) |
|
||||
| 503 | socket no listo (status != open) |
|
||||
Reference in New Issue
Block a user