Guaba Connect · contrato v1
Conectá tu ecommerce a WhatsApp en dos endpoints
Guaba atiende WhatsApp por el negocio: catálogo, agente con IA y pedidos. Con Connect, Guaba lee el catálogo de tu plataforma y le empuja los pedidos que se generan por WhatsApp — tu ecommerce sigue siendo la fuente de verdad. Esto es todo lo que hay que implementar.
Cómo funciona
1
Guaba lee tu catálogo
Consulta GET /catalogo cada pocos minutos. Productos, variantes, precios y stock — tu plataforma manda.
2
Guaba te empuja pedidos
Cuando un cliente confirma por WhatsApp, llega un POST /pedidos firmado. Respondés con tu ID de pedido.
3
Vos reportás estados
Confirmado, enviado, entregado… se lo avisás a Guaba para que el agente le responda al cliente.
Credenciales
El negocio las genera en su panel de Guaba (Configuración → Guaba Connect) y te las pasa. Se muestran una sola vez y se pueden rotar.
| Credencial | Dirección | Para qué |
|---|---|---|
glt_… token de lectura | Guaba → vos | Header Authorization: Bearer en cada pull y push. Verificalo siempre. |
gfs_… secreto de firma | Guaba → vos | HMAC-SHA256 del cuerpo crudo en X-Guaba-Signature. Confirmá que el pedido viene de Guaba. |
gak_… api key | vos → Guaba | Header X-Guaba-Api-Key al reportar estados de pedido. |
1 · Tu catálogo — GET {base_url}/catalogo
Query params: page (desde 1), per_page (máx. 200) y updated_since (ISO-8601, opcional — si lo soportás, los sync son incrementales).
{
"productos": [
{
"id": "SKU-123",
"nombre": "Queque de zanahoria",
"descripcion": "Para 8 personas",
"precio": 8500,
"moneda": "CRC",
"imagen_url": "https://tu-tienda.com/img/queque.jpg",
"categoria": "Queques",
"disponible": true,
"stock": 5,
"variantes": [
{ "id": "SKU-123-P", "nombre": "pequeño", "precio": 4500, "stock": 2, "disponible": true },
{ "id": "SKU-123-G", "nombre": "grande", "precio": 8500, "stock": null, "disponible": true }
]
}
],
"pagina": 1,
"total_paginas": 3
}- ·
ides TU identificador (SKU): estable y único — con él te referenciamos productos en los pedidos. - ·
precioen la unidad menor ya resuelta (colones enteros para CRC). - ·
stock: null= no controlás stock de ese ítem. - ·
varianteses opcional y plana (presentaciones con precio propio). - ·
descripcion,imagen_urlycategoriason opcionales.
2 · Tus pedidos — POST {base_url}/pedidos
Headers: Authorization: Bearer · X-Guaba-Signature: sha256=<hmac> · X-Guaba-Idempotency-Key: <uuid>
{
"pedido_guaba_id": "9f2c1a37-4b2e-4c1d-9e8f-1a2b3c4d5e6f",
"creado_en": "2026-07-10T14:03:00Z",
"cliente": { "nombre": "Tania", "telefono": "+50663270771" },
"items": [
{
"producto_id": "SKU-123",
"variante_id": "SKU-123-G",
"nombre": "Queque de zanahoria (grande)",
"cantidad": 1,
"precio_unitario": 8500
}
],
"total": 8500,
"moneda": "CRC",
"notas": "Retira el sábado 10am",
"canal": "whatsapp"
}
// Tu respuesta esperada (201):
{ "pedido_id": "WC-4581" }- · Idempotencia obligatoria: misma
X-Guaba-Idempotency-Keydos veces → mismopedido_id, sin duplicar (reintentamos hasta 5 veces con backoff ante timeouts). - · El pago NO viaja por acá: el pedido llega «pendiente», como uno telefónico.
- ·
4xx= no reintentamos (queda para revisión humana) ·5xx/timeout = reintentamos.
3 · Estados — POST …/api/connect/pedidos/{pedido_guaba_id}
Opcional pero recomendado: sin esto, el agente no puede contestarle al cliente en qué va su pedido. Header X-Guaba-Api-Key.
{ "estado": "confirmado", "pedido_id": "WC-4581" }
// estados válidos: confirmado · preparando · entregado · cancelado
// respuestas: 200 ok · 401 api key · 404 pedido · 422 estado inválidoChecklist de implementación
- GET /catalogo con paginación (y updated_since si podés).
- Verificás Authorization: Bearer en ambos endpoints.
- POST /pedidos idempotente por X-Guaba-Idempotency-Key.
- Verificás la firma X-Guaba-Signature (HMAC-SHA256 del cuerpo crudo).
- (Recomendado) reportás cambios de estado con tu api key.
- Todo sobre HTTPS con certificado válido.
🤖 Implementalo con IA
¿Usás Claude, Cursor o Copilot? Copiá el prompt, pegalo en tu asistente junto con tu código, reemplazá los valores marcados con {{…}} y dejá que implemente tu lado del contrato. Cada prompt es autocontenido.
1 · Exponé tu catálogoGET /catalogo con Bearer y paginación, mapeando tu esquema de productos.
⚠️ ANTES DE PEGAR, REEMPLAZÁ ESTOS VALORES:
- {{TU_STACK}}: tu stack (ej: "Laravel 11 + MySQL", "Node/Express + Postgres", "WordPress/WooCommerce como plugin", "Django")
- {{URL_BASE}}: la URL base que vas a exponer (ej: https://mitienda.com/api/guaba)
- {{TOKEN_LECTURA}}: el token que te dio Guaba (empieza con glt_)
- {{SECRETO_FIRMA}}: el secreto de firma que te dio Guaba (empieza con gfs_)
- {{API_KEY}}: la api key que te dio Guaba (empieza con gak_)
Guardá los tres como variables de entorno/secretos, nunca en el código.
Sos un desarrollador senior. Implementá en mi proyecto ({{TU_STACK}}) un endpoint HTTP para integrarme con Guaba Commerce (una plataforma que atiende WhatsApp por mi negocio y necesita LEER mi catálogo).
## Qué construir
GET {{URL_BASE}}/catalogo
1. AUTENTICACIÓN: exigí el header `Authorization: Bearer {{TOKEN_LECTURA}}` (compará contra la variable de entorno; si no coincide, respondé 401 JSON). Usá comparación de strings de tiempo constante si el lenguaje lo permite.
2. QUERY PARAMS: `page` (entero desde 1, default 1), `per_page` (entero, máximo 200, default 100), `updated_since` (ISO-8601 opcional — si viene, devolvé solo productos modificados desde esa fecha; si no podés implementarlo fácil, ignoralo).
3. RESPUESTA 200 (Content-Type application/json), mapeando MI esquema de productos a EXACTAMENTE este formato:
{
"productos": [
{
"id": "SKU-123", // MI identificador estable y único (SKU o ID interno) — string
"nombre": "Queque de zanahoria", // requerido, máx 120 chars
"descripcion": "Para 8 personas", // opcional, máx 500
"precio": 8500, // requerido, número en la unidad menor ya resuelta (colones enteros para CRC)
"moneda": "CRC", // opcional, ISO-4217, default CRC
"imagen_url": "https://...", // opcional, URL absoluta https
"categoria": "Queques", // opcional, máx 80
"disponible": true, // opcional, default true — false lo oculta de WhatsApp
"stock": 5, // opcional; null u omitido = no controlo stock
"variantes": [ // opcional — presentaciones/tamaños con precio propio (lista PLANA, sin matriz de atributos)
{ "id": "SKU-123-P", "nombre": "pequeño", "precio": 4500, "stock": 2, "disponible": true }
]
}
],
"pagina": 1,
"total_paginas": 3
}
## Criterios de aceptación
- Solo productos que quiero vender por WhatsApp (publicados/activos).
- Los `id` (producto y variante) son estables entre llamadas: Guaba me referencia pedidos con ellos.
- Paginación correcta: `total_paginas` real; página fuera de rango → `productos: []`.
- Sin el Bearer correcto → 401. Todo sobre HTTPS.
- Respuesta en menos de 8 segundos (Guaba corta ahí): si mi catálogo es lento, agregá caché de ~1 minuto.
Mostrame el código completo, dónde ubicarlo en mi proyecto y cómo probarlo con curl.2 · Recibí los pedidos de WhatsAppPOST /pedidos con verificación de firma HMAC e idempotencia persistente.
⚠️ ANTES DE PEGAR, REEMPLAZÁ ESTOS VALORES:
- {{TU_STACK}}: tu stack (ej: "Laravel 11 + MySQL", "Node/Express + Postgres", "WordPress/WooCommerce como plugin", "Django")
- {{URL_BASE}}: la URL base que vas a exponer (ej: https://mitienda.com/api/guaba)
- {{TOKEN_LECTURA}}: el token que te dio Guaba (empieza con glt_)
- {{SECRETO_FIRMA}}: el secreto de firma que te dio Guaba (empieza con gfs_)
- {{API_KEY}}: la api key que te dio Guaba (empieza con gak_)
Guardá los tres como variables de entorno/secretos, nunca en el código.
Sos un desarrollador senior. Implementá en mi proyecto ({{TU_STACK}}) el endpoint que RECIBE los pedidos que Guaba Commerce genera por WhatsApp y los crea como pedidos reales en mi plataforma.
## Qué construir
POST {{URL_BASE}}/pedidos
1. AUTENTICACIÓN (dos capas, ambas obligatorias):
a. Header `Authorization: Bearer {{TOKEN_LECTURA}}` → si no coincide, 401.
b. Header `X-Guaba-Signature: sha256=<hex>` = HMAC-SHA256 del CUERPO CRUDO del request (los bytes exactos, ANTES de parsear el JSON) con la clave {{SECRETO_FIRMA}}. Calculá el HMAC del cuerpo recibido y comparalo con timing-safe equal → si no coincide, 401. OJO: necesitás acceso al raw body (en Express: express.raw o verify callback; en Laravel: $request->getContent()).
2. IDEMPOTENCIA (crítico): header `X-Guaba-Idempotency-Key: <uuid>`. Guardá la key con el ID del pedido creado (tabla o cache persistente). Si llega OTRA VEZ la misma key → respondé 201 con el MISMO pedido_id SIN crear un duplicado. Guaba reintenta ante timeouts hasta 5 veces.
3. CUERPO que recibís:
{
"pedido_guaba_id": "9f2c1a37-4b2e-4c1d-9e8f-1a2b3c4d5e6f", // UUID del pedido en Guaba
"creado_en": "2026-07-10T14:03:00Z",
"cliente": { "nombre": "Tania", "telefono": "+50663270771" },
"items": [
{
"producto_id": "SKU-123", // MI id (el que expuse en /catalogo)
"variante_id": "SKU-123-G", // solo si el producto tiene variantes
"nombre": "Queque de zanahoria (grande)",
"cantidad": 1,
"precio_unitario": 8500
}
],
"total": 8500,
"moneda": "CRC",
"notas": "Retira el sábado 10am",
"canal": "whatsapp"
}
4. Creá el pedido en MI sistema como «pendiente de pago/confirmación» (igual que un pedido telefónico — el pago NO viaja por acá), asociando cliente por teléfono si existe.
5. RESPUESTA 201: { "pedido_id": "WC-4581" } — MI identificador del pedido creado.
6. ERRORES: 4xx si el pedido es inválido (Guaba NO reintenta y lo marca para revisión humana) · 5xx solo ante fallas transitorias (Guaba SÍ reintenta).
## Criterios de aceptación
- Verificación de firma con el cuerpo CRUDO (un espacio de diferencia rompe el HMAC).
- Segunda llamada con la misma idempotency key NO crea segundo pedido y devuelve el mismo pedido_id.
- Producto_id inexistente en mi catálogo → 422 con mensaje claro (no 500).
- Respuesta en menos de 8 segundos.
Mostrame el código completo, la migración/tabla para las idempotency keys, y cómo probarlo con curl (incluí cómo generar la firma HMAC de prueba en una línea).3 · Reportá los estados a GuabaEl hook que avisa a Guaba cuando tu pedido avanza (confirmado, entregado…).
⚠️ ANTES DE PEGAR, REEMPLAZÁ ESTOS VALORES:
- {{TU_STACK}}: tu stack (ej: "Laravel 11 + MySQL", "Node/Express + Postgres", "WordPress/WooCommerce como plugin", "Django")
- {{URL_BASE}}: la URL base que vas a exponer (ej: https://mitienda.com/api/guaba)
- {{TOKEN_LECTURA}}: el token que te dio Guaba (empieza con glt_)
- {{SECRETO_FIRMA}}: el secreto de firma que te dio Guaba (empieza con gfs_)
- {{API_KEY}}: la api key que te dio Guaba (empieza con gak_)
Guardá los tres como variables de entorno/secretos, nunca en el código.
Sos un desarrollador senior. Agregá a mi proyecto ({{TU_STACK}}) las llamadas que le avisan a Guaba Commerce cuando un pedido cambia de estado en mi plataforma, para que su agente de WhatsApp pueda responderle al cliente «tu pedido va en camino».
## Qué construir
Cada vez que un pedido QUE VINO DE GUABA cambie de estado en mi sistema (lo reconocés porque lo creó el endpoint /pedidos y guardaste su `pedido_guaba_id`), hacé:
POST https://guaba.tech/api/connect/pedidos/{pedido_guaba_id}
Headers: `X-Guaba-Api-Key: {{API_KEY}}` · `Content-Type: application/json`
Body: { "estado": "confirmado", "pedido_id": "WC-4581" }
- Estados válidos (mapeá los míos a estos 4): "confirmado" (acepté el pedido), "preparando" (en producción/armado), "entregado" (el cliente lo tiene), "cancelado".
- `pedido_id` es MI identificador (opcional pero recomendado).
- Respuestas: 200 ok · 401 api key mala · 404 el pedido no existe en Guaba · 422 estado inválido.
## Criterios de aceptación
- El hook se dispara SOLO para pedidos con pedido_guaba_id (los demás pedidos de mi tienda no llaman a Guaba).
- Ante error de red o 5xx: reintentá con backoff (o encolá) — no bloquees mi flujo de pedidos por esto.
- Ante 401/404/422: logueá y NO reintentes (es un error de datos, no transitorio).
- La api key vive en variables de entorno.
Mostrame dónde engancharlo en mi flujo de cambio de estado (observer/hook/señal según mi stack) y el código completo con manejo de errores.¿Dudas o feedback del contrato?
Estamos validando la v1 con los primeros integradores — tu opinión la moldea.
Escribinos →