API de catálogo de CRMwHATA
Permite que tu sitio web (o cualquier sistema) cree, actualice y desactive productos en CRMwHATA. Los productos aparecen en Productos y el agente de IA los usa al responder. Disponible en el plan Empresarial.
Autenticación
- En CRMwHATA ve a Configuración → Integraciones → Conectores → API de catálogo y crea una clave (nombre y vencimiento). Se muestra una sola vez.
- Envía la clave en cada petición:
Authorization: Bearer <clave>. Nunca en la URL. Sólo HTTPS.
Base: https://api.crmwhata.com/api/v1/catalogo
Endpoints
| Método | Ruta | Qué hace |
|---|---|---|
PUT |
/productos/{external_id} |
Crea o actualiza un producto. 201 al crear, 200 al actualizar o si no cambió nada. |
POST |
/productos/lote |
Hasta 50 productos en una llamada. |
POST |
/productos/activos |
Desactiva todo producto creado por la API que no esté en tu lista de ids vivos. |
DELETE |
/productos/{external_id} |
Desactiva el producto (no lo borra). |
GET |
/productos/{external_id} |
Devuelve el producto. |
GET |
/productos |
Lista paginada (50 por página, ?page=2). |
external_id es el id del producto en tu sistema: 1–64 caracteres, letras, números,
_, -, .. La misma regla se aplica en la URL y en cada producto del lote. En CRMwHATA
el código del producto será WEB-{external_id}.
Respuestas
Todas las respuestas correctas llevan "success": true.
-
PUT /productos/{external_id}:{ "success": true, "data": { "estado": "creado|actualizado|sin_cambios", "producto": { ...producto } } }. El código es 201 sólo cuandoestadoescreado; en los demás casos, 200. -
GET /productos/{external_id}yDELETE /productos/{external_id}:{ "success": true, "data": { ...producto } }. -
GET /productos:{ "success": true, "data": [ ...productos ], "meta": { "total", "pagina", "por_pagina" } }. Son 50 por página; usa?page=Npara avanzar. -
POST /productos/lote:{ "success": true, "data": { "<external_id>": <resultado> } }, donde<resultado>es una de estas formas:{ "estado": "creado|actualizado|sin_cambios", "id": 123 }{ "estado": "desactivado" }(sinid){ "estado": "error", "errores": { ... } }(validación, por campo){ "estado": "error", "mensaje": "..." }(choque de SKU)
Si un mismo
external_idaparece dos veces en el lote, se aplica el último y la entrada lleva unmensajeque lo indica. Un problema de la petición completa (listaproductosvacía, más de 50 productos,external_idausente o mal formado) responde 422 para toda la llamada y no aplica nada.
El objeto producto tiene esta forma:
{ "id": 987, "external_id": "123", "sku": "WEB-123", "nombre": "Apartamento 2 hab. en Naco",
"precio": 1450, "moneda": "USD", "categoria": "Alquiler", "descripcion": "...", "activo": true,
"url": "https://inmobiliaria.do/apartamentos/123", "imagenes": ["https://inmobiliaria.do/fotos/123-1.jpg"],
"atributos": { "habitaciones": 2 }, "synced_at": "2026-10-15T12:00:00Z" }
Cuerpo del producto
{
"nombre": "Apartamento 2 hab. en Naco",
"precio": 1450,
"moneda": "USD",
"categoria": "Alquiler",
"descripcion": "Apartamento amueblado en torre con piscina...",
"activo": true,
"url": "https://inmobiliaria.do/apartamentos/123",
"imagenes": ["https://inmobiliaria.do/fotos/123-1.jpg", "https://inmobiliaria.do/fotos/123-2.jpg"],
"atributos": { "habitaciones": 2, "banos": 2, "metros": 110, "zona": "Naco" }
}
| Campo | Reglas |
|---|---|
nombre |
obligatorio, máx. 255 |
precio |
opcional, número ≥ 0 |
moneda |
opcional, código ISO de 3 letras (USD, DOP) |
categoria |
opcional, máx. 100 |
descripcion |
opcional, máx. 2000 (CRMwHATA añade al final una línea de atributos, separada por --- Detalles ---, que cuenta para ese límite) |
activo |
opcional, por defecto true |
url |
opcional, enlace al producto en tu sitio |
imagenes |
0 a 5 URLs https |
atributos |
objeto plano, máx. 30 claves. Cada clave tiene de 1 a 50 caracteres. Los valores son texto (máx. 200 caracteres), número o booleano; no se admiten listas ni objetos anidados. |
Un atributos en forma de lista (["a","b"]), una clave fuera de 1–50 caracteres o un valor
anidado se rechaza así:
- En
PUT: 422 conerror_code: "validation". - En
POST /productos/lote: sólo ese producto queda comoerror, conerrores.atributos(en el caso de un valor anidado, el detalle aparece bajoatributos.<clave>). La respuesta HTTP sigue siendo 200 y los demás productos del lote se aplican.
Notas:
- Las URLs de
imagenesyurldeben ser públicas (https y host público; se rechazan direcciones internas). - Usa fotos JPG o PNG; WhatsApp no acepta WebP.
- El inventario (cantidad) se gestiona en CRMwHATA; la API no lo modifica.
- La búsqueda por foto del agente no cubre productos creados por API.
- Llama a la API desde tu servidor, nunca desde el navegador: la clave no debe exponerse.
No hay límite de tamaño del cuerpo impuesto por la API más allá del que fije el servidor web.
Campos desconocidos → 422. Ejemplo para una tienda de ropa:
{ "nombre": "Camisa lino blanca", "precio": 1850, "moneda": "DOP", "categoria": "Camisas",
"imagenes": ["https://tienda.do/img/camisa-lino.jpg"],
"atributos": { "talla": "M", "color": "blanco", "material": "lino" } }
Lote
{ "desactivar_ausentes": true,
"productos": [ { "external_id": "123", "nombre": "...", "precio": 1450 }, { "external_id": "124", "nombre": "..." } ] }
Cada producto del lote lleva su propio external_id, validado con la misma regla que en la URL.
Con desactivar_ausentes: true, todo producto creado por la API que no venga en la lista se
desactiva. Sólo tiene sentido cuando todo tu catálogo cabe en una sola llamada (50 productos o menos).
Recomendado: envía tus productos con PUT (o en lotes de hasta 50 con /productos/lote) y, al terminar, llama a /productos/activos con la lista completa de ids vivos.
Productos activos
{ "external_ids": ["123", "124", "125"] }
POST /productos/activos recibe de 1 a 10,000 ids, cada uno con la misma regla que external_id. Desactiva todo producto creado por la API que esté activo y no aparezca en la lista; no crea ni reactiva nada. Respuesta:
{ "success": true, "data": { "desactivados": 3, "activos": 120 } }
La respuesta indica el resultado por external_id, con uno de estos estados:
creado: el producto se creó.actualizado: el producto existía y cambió.sin_cambios: el producto existía y no cambió nada.desactivado: el producto quedó inactivo porque no venía en la lista (desactivar_ausentes).error: el producto no se aplicó. Si es un error de validación, la entrada traeerrorescon el detalle por campo. Si es un choque de SKU (WEB-{external_id}ya existe como producto creado a mano), la entrada traemensaje.
Un error en un producto no impide que se apliquen los demás del lote.
Errores
| HTTP | error_code |
Motivo |
|---|---|---|
| 401 | — | clave ausente, inválida, vencida o revocada |
| 403 | token_scope |
la clave no es de la API de catálogo |
| 403 | plan_required |
el plan no incluye la API de catálogo |
| 404 | not_found |
external_id desconocido |
| 409 | sku_en_uso |
WEB-{external_id} ya existe como producto creado a mano |
| 422 | validation |
datos inválidos (errors por campo) |
| 429 | — | más de 120 peticiones por minuto con la misma clave (cabecera Retry-After) |
| 429 | too_many_attempts |
tras 10 respuestas 401 en un minuto desde la misma IP (cabecera Retry-After). Se responde antes de autenticar, incluso con una clave válida, hasta que pase el minuto |
Qué pasa en CRMwHATA
- El producto aparece en Productos con la insignia API. Nombre, precio, categoría, descripción, imágenes y activo sólo cambian desde tu sitio; descuentos y puntos de venta se editan en CRMwHATA.
- Desactivar no borra: el producto queda inactivo y vuelve a activarse si lo envías de nuevo.
- Las imágenes se muestran desde tu URL; no se descargan.
Ejemplo con curl
curl -X PUT "https://api.crmwhata.com/api/v1/catalogo/productos/123" \
-H "Authorization: Bearer $CRMWHATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nombre":"Apartamento 2 hab. en Naco","precio":1450,"moneda":"USD","categoria":"Alquiler","imagenes":["https://inmobiliaria.do/fotos/123-1.jpg"],"atributos":{"habitaciones":2,"zona":"Naco"}}'