API REST

Integre PolpoChat con su CRM, ERP o e-commerce. Envíe mensajes, gestione tickets y sincronice datos programáticamente.

Base URL: https://<deployment>.convex.site Auth: Bearer Token Formato: JSON

Autenticación

Todos los endpoints requieren un Bearer Token en el header Authorization. Cada conexión de WhatsApp tiene su propio token de 48 caracteres.

Header
Authorization: Bearer <TOKEN>

Gestión de tokens

Los tokens se gestionan desde API Externa en la plataforma (solo administradores):

AcciónDescripción
GenerarCrea un token de 48 caracteres alfanuméricos único por conexión
RotarGenera un nuevo token y reemplaza el anterior (el anterior deja de funcionar)
EliminarRevoca el token — la API deja de aceptar peticiones para esa conexión
El token está vinculado a una conexión de WhatsApp específica y a su empresa. Todas las operaciones se ejecutan en el contexto de esa empresa.

Error de autenticación 401

JSON
{ "error": "Acceso no permitido" }

Base URL

La URL base de la API se deriva de la URL de su deployment de Convex, reemplazando .convex.cloud por .convex.site:

URL
https://<su-deployment>.convex.site

Puede encontrar su URL exacta en la sección API Externa dentro de la plataforma.

POST /api/messages/send

Envía un mensaje de WhatsApp a un número. Si el contacto o ticket no existen, se crean automáticamente.

Parámetros

Content-Type: application/json o multipart/form-data (para archivos)

ParámetroTipoDescripción
numberstringrequeridoNúmero de teléfono destino (se normaliza automáticamente)
bodystringopcionalTexto del mensaje
mediaUrlstringopcionalURL pública del archivo a enviar
mediaTypestringopcional"image", "video", "audio" o "document"
mediaMimestringopcionalMIME type del archivo (ej: "image/jpeg")
mediaKeystringopcionalKey de S3 si el archivo ya está almacenado
closeTicketbooleanopcionalSi true, cierra el ticket después de enviar

Ejemplo

curl
curl -X POST 'https://<deployment>.convex.site/api/messages/send' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{
    "number": "50688881234",
    "body": "Hola, su pedido está listo para recoger."
  }'

Ejemplo con archivo

curl
curl -X POST 'https://<deployment>.convex.site/api/messages/send' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{
    "number": "50688881234",
    "body": "Aquí está su factura",
    "mediaUrl": "https://ejemplo.com/factura.pdf",
    "mediaType": "document",
    "mediaMime": "application/pdf"
  }'

Respuesta exitosa 200

JSON
{
  "success": true,
  "mensagem": "Mensaje enviado",
  "ticketId": "<ticket-id>",
  "messageId": "<message-id>"
}

Errores 400

JSON
// Falta número
{ "error": "O número é obrigatório" }

// JSON malformado
{ "error": "Invalid JSON" }
GET /api/tickets/:ticketId

Obtiene los detalles de un ticket incluyendo su dataWebhook.

Ejemplo

curl
curl 'https://<deployment>.convex.site/api/tickets/<ticketId>' \
  -H 'Authorization: Bearer <TOKEN>'

Respuesta exitosa 200

JSON
{
  "success": true,
  "ticket": {
    "id": "<ticket-id>",
    "status": "open",
    "contactId": "<contact-id>",
    "whatsappId": "<whatsapp-id>",
    "companyId": "<company-id>",
    "dataWebhook": {},
    "lastMessage": "Hola, necesito ayuda",
    "updatedAt": 1714400000000
  }
}

Error 404

JSON
{ "error": "Ticket não encontrado" }
PATCH /api/tickets/:ticketId

Actualiza el estado y/o la cola de un ticket.

Parámetros

ParámetroTipoDescripción
statusstringopcional*"open", "pending" o "closed"
queueIdstring | nullopcional*ID de la cola destino, o null para quitar
Al menos uno de status o queueId es requerido. Al cerrar un ticket se limpian automáticamente integraciones, bots y flows activos.

Ejemplo — cerrar ticket

curl
curl -X PATCH 'https://<deployment>.convex.site/api/tickets/<ticketId>' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{ "status": "closed" }'

Respuesta exitosa 200

JSON
{
  "success": true,
  "message": "ticket atualizado",
  "ticket": { /* ticket actualizado */ }
}
GET /api/tickets/:ticketId/recent-messages

Lista los mensajes más recientes de un ticket.

Query parameters

ParámetroTipoDefaultDescripción
limitnumber3Cantidad de mensajes a retornar

Ejemplo

curl
curl 'https://<deployment>.convex.site/api/tickets/<ticketId>/recent-messages?limit=5' \
  -H 'Authorization: Bearer <TOKEN>'

Respuesta exitosa 200

JSON
{
  "success": true,
  "messages": [
    {
      "body": "Hola, necesito ayuda",
      "fromMe": false,
      "createdAt": "2026-04-29T10:00:00.000Z"
    }
  ]
}
GET /api/tickets/inactive-pending

Lista tickets pendientes que llevan cierto tiempo sin actividad. Útil para jobs de follow-up automático.

Query parameters

ParámetroTipoDefaultDescripción
minutesnumber40Minutos de inactividad (alias: inactivityMinutes)
limitnumber10Máximo de resultados (tope: 100)

Ejemplo

curl
curl 'https://<deployment>.convex.site/api/tickets/inactive-pending?minutes=30&limit=20' \
  -H 'Authorization: Bearer <TOKEN>'

Respuesta exitosa 200

JSON
{
  "success": true,
  "tickets": [
    {
      "ticketId": "<id>",
      "updatedAt": "2026-04-29T09:20:00.000Z",
      "queueId": "<queue-id>",
      "contactNumber": "50688881234",
      "contactName": "María González",
      "dataWebhook": {}
    }
  ]
}
GET /api/tickets/stale-follow-up

Lista tickets cuyo estado de follow-up en dataWebhook puede limpiarse por antigüedad.

Query parameters

ParámetroTipoDefaultDescripción
minutesnumber60Antigüedad en minutos (alias: ageMinutes)
limitnumber100Máximo de resultados (tope: 250)

Ejemplo

curl
curl 'https://<deployment>.convex.site/api/tickets/stale-follow-up?minutes=120' \
  -H 'Authorization: Bearer <TOKEN>'

Respuesta exitosa 200

JSON
{
  "success": true,
  "tickets": [
    {
      "ticketId": "<id>",
      "staleAt": "2026-04-29T08:00:00.000Z"
    }
  ]
}
PATCH /api/tickets/:ticketId/data-webhook

Hace un deep merge de JSON personalizado en el campo dataWebhook del ticket. Ideal para almacenar metadata de integraciones externas.

Parámetros

El body es cualquier objeto JSON válido. Se hace merge profundo con el dataWebhook existente.

Límite de tamaño: 64 KB máximo para el dataWebhook resultante. Los arrays se reemplazan (no se concatenan). Keys con valor null eliminan esa key.

Ejemplo

curl
curl -X PATCH 'https://<deployment>.convex.site/api/tickets/<ticketId>/data-webhook' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{
    "orderId": "ORD-4521",
    "status": "shipped",
    "tracking": "CR123456789"
  }'

Respuesta exitosa 200

JSON
{
  "success": true,
  "message": "dataWebhook atualizado",
  "ticketId": "<ticket-id>",
  "dataWebhook": {
    "orderId": "ORD-4521",
    "status": "shipped",
    "tracking": "CR123456789"
  }
}
POST /api/tags/sync-external

Agrega etiquetas a un ticket. Resuelve por nombre (case-insensitive) o por ID.

Parámetros

ParámetroTipoDescripción
ticketIdstringrequeridoID del ticket
tagsarrayrequeridoArray de objetos tag (no vacío)
tags[].namestringopcional*Nombre del tag (case-insensitive)
tags[].idstringopcional*ID del tag
Cada tag debe tener al menos name o id. La operación es idempotente — agregar un tag que ya existe no genera error.

Ejemplo

curl
curl -X POST 'https://<deployment>.convex.site/api/tags/sync-external' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{
    "ticketId": "<ticket-id>",
    "tags": [
      { "name": "VIP" },
      { "name": "Urgente" }
    ]
  }'

Respuesta exitosa 200

JSON
{
  "success": true,
  "message": "Tags sincronizados",
  "ticketId": "<ticket-id>",
  "tagsAdded": ["<tag-id-1>", "<tag-id-2>"]
}

Errores 400 404

JSON
// Faltan campos
{ "error": "ticketId e tags são obrigatórios" }

// Tag no encontrada
{ "error": "Tag não encontrada: VIP" }
GET /products

Lista productos del catálogo con paginación. Este endpoint debe ser implementado por conectores custom siguiendo la especificación BYOEcom.

Los endpoints de BYOEcom son implementados por el ecommerce externo (no por PolpoChat). Esta documentación describe el contrato que su conector custom debe cumplir.

Query parameters

ParámetroTipoDefaultDescripción
offsetnumber0Posición inicial (mínimo 0)
limitnumber20Cantidad de productos (1-100)

Headers requeridos

Header
Authorization: Bearer <API_KEY>

Respuesta exitosa 200

JSON
{
  "status": "success",
  "data": [
    {
      "id": "prod_001",
      "name": "Camiseta Básica",
      "description": "Camiseta 100% algodón",
      "price": 15.99,
      "currency": "USD",
      "imageUrl": "https://ejemplo.com/img/camiseta.jpg",
      "category": "Ropa",
      "variant": "Talla M, Azul",
      "available": true
    }
  ],
  "error": null
}
GET /products/:id

Obtiene un producto específico por su identificador único.

Respuesta exitosa 200

JSON
{
  "status": "success",
  "data": {
    "id": "prod_001",
    "name": "Camiseta Básica",
    "description": "Camiseta 100% algodón, disponible en varios colores",
    "price": 15.99,
    "currency": "USD",
    "imageUrl": "https://ejemplo.com/img/camiseta.jpg",
    "category": "Ropa",
    "variant": "Talla M, Azul",
    "available": true
  },
  "error": null
}

No encontrado 404

JSON
{
  "status": "error",
  "data": null,
  "error": "Product not found"
}
POST /orders

Crea una nueva orden en el ecommerce con los productos seleccionados por el cliente.

Parámetros (body JSON)

ParámetroTipoDescripción
lineItemsarrayrequerido1-100 items, cada uno con productId y quantity
customerNamestringrequeridoNombre del cliente
customerContactstringrequeridoTeléfono o email del cliente
deliveryAddressstringrequeridoDirección de entrega
paymentMethodstringrequeridoMétodo de pago preferido
discountCodestringopcionalCódigo de descuento a aplicar

Ejemplo

JSON
{
  "lineItems": [
    { "productId": "prod_001", "quantity": 2 },
    { "productId": "prod_005", "quantity": 1 }
  ],
  "customerName": "María González",
  "customerContact": "+50688881234",
  "deliveryAddress": "San José, Escazú, 200m norte del parque",
  "paymentMethod": "sinpe_movil"
}

Respuesta exitosa 200

JSON
{
  "status": "success",
  "data": {
    "orderId": "ORD-2026-0042",
    "status": "pending",
    "total": 47.97,
    "currency": "USD",
    "createdAt": "2026-05-11T14:30:00Z"
  },
  "error": null
}
GET /orders/:id

Obtiene los detalles y estado actual de una orden existente.

Respuesta exitosa 200

JSON
{
  "status": "success",
  "data": {
    "orderId": "ORD-2026-0042",
    "status": "shipped",
    "lineItems": [
      { "name": "Camiseta Básica", "quantity": 2, "price": 15.99 }
    ],
    "total": 47.97,
    "currency": "USD",
    "createdAt": "2026-05-11T14:30:00Z"
  },
  "error": null
}
POST /api/ecommerce/webhook

Endpoint de PolpoChat que recibe eventos de su ecommerce (cambios de estado de orden, etc.). Cuando se recibe un evento válido, PolpoChat envía automáticamente una notificación al cliente por WhatsApp.

Este endpoint es expuesto por PolpoChat (no por su ecommerce). Configure la URL del webhook en su plataforma de ecommerce para enviar eventos aquí.

Headers requeridos

HeaderDescripción
X-Webhook-SecretSecret único por empresa (se genera en la configuración de BYOEcom)
Content-Typeapplication/json

Payload

JSON
{
  "event": "order_status_changed",
  "orderId": "ORD-2026-0042",
  "newStatus": "shipped",
  "timestamp": "2026-05-11T16:00:00Z"
}

Eventos soportados

EventoDescripción
order_status_changedEl estado de una orden cambió
order_createdSe creó una nueva orden externamente
order_cancelledUna orden fue cancelada

Respuesta exitosa 200

JSON
{ "success": true, "message": "Event processed" }

Error de autenticación 401

JSON
{ "error": "Invalid webhook secret" }

¿Listo para integrar su ecommerce?

Implemente los 5 endpoints de la especificación BYOEcom y conecte cualquier plataforma de ecommerce con WhatsApp en minutos.

GET /health
GET /products
GET /products/:id
POST /orders
GET /orders/:id