guaba·commerceConnect · guía técnica

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. Si tu plataforma no es WooCommerce, esto es todo lo que hay que implementar.

¿Tu tienda es WooCommerce? Conectala sin plugin

Si tu tienda usa WooCommerce no hace falta programar nada ni instalar un plugin: el dueño la conecta desde su panel de Guaba y WooCommerce le pide autorizar la conexión. Está disponible desde el plan Crece y solo el dueño del negocio puede conectarla.

  1. En Guaba entrá a Configuración → Guaba Connect y buscá «Conectar con WooCommerce».
  2. Escribí la dirección de tu tienda, la misma con la que entrás a tu WordPress. Tiene que empezar con https.
  3. Guaba te lleva a tu WordPress, que te pregunta si querés darle acceso a la app «Guaba». Aprobá y volvés a Guaba.
  4. Guaba revisa cómo cobra impuestos tu tienda, activa el aviso de cambios de estado y trae tus productos. Tarda menos de un minuto; después el catálogo se actualiza solo cada unos 10 minutos.

Qué permiso pide y para qué

WooCommerce solo ofrece llaves de Lectura/Escritura; no existe una más chica. Guaba la usa únicamente para:

  • · Leer productos, precios, fotos, existencias y la configuración de impuestos. Ojo: la misma llave permite ver todos los pedidos y los datos de los clientes de la tienda, y WooCommerce no deja limitarla. Guaba lee pedidos solo para no duplicar uno que ya mandó y mantener su estado al día.
  • · Crear en tu tienda el pedido que el cliente armó por WhatsApp. Entra con estado «pendiente de pago»: el pago no viaja por Guaba y lo confirmás vos.
  • · Crear un aviso (webhook order.updated) para enterarse cuando cambia el estado de un pedido.

Guaba no toca tus pagos, no borra nada de tu tienda y guarda las llaves cifradas. Los estados de WooCommerce se traducen así: on-hold → confirmado · processing → preparando · completed → entregado · cancelled, refunded y failed → cancelado. Con esta conexión no se usan las credenciales de abajo: los estados llegan por el aviso firmado de tu propia tienda.

Al desconectar

WooCommerce no deja que otra app revoque su propia llave. Al desconectar, Guaba borra su copia, pero la llave sigue viva en tu tienda hasta que la revoques en WooCommerce → Ajustes → Avanzado → API REST (la que dice «Guaba - API»). Guaba te lo recuerda en Configuración hasta que lo hagas.

Si la conexión automática no te funciona

En la misma pantalla, «¿No te funciona? Pegá tus llaves a mano»: creás en tu WordPress, en WooCommerce → Ajustes → Avanzado → API REST → Añadir clave, una llave con permisos Lectura/Escritura y pegás las dos claves (ck_… y cs_…). Si tu plataforma no es WooCommerce, seguí con el contrato de abajo. Existe también un plugin de WordPress que implementará ese mismo contrato, pero todavía está en desarrollo.

Cualquier otra plataforma: el contrato

Para una tienda propia o cualquier plataforma sin conexión directa, Guaba habla un contrato de dos endpoints (más uno opcional de estados). 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.

CredencialDirecciónPara qué
glt_… token de lecturaGuaba → vosHeader Authorization: Bearer en cada pull y push. Verificalo siempre.
gfs_… secreto de firmaGuaba → vosHMAC-SHA256 del cuerpo crudo en X-Guaba-Signature. Confirmá que el pedido viene de Guaba.
gak_… api keyvos → GuabaHeader X-Guaba-Api-Key al reportar estados de pedido.

1 · Tu catálogo — GET {base_url}/catalogo

Query params: page (desde 1) y per_page (máx. 200 ítems por página; Guaba pide 100).

{
  "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
}
  • · id es TU identificador (SKU): estable y único — con él te referenciamos productos en los pedidos.
  • · precio en la unidad menor ya resuelta (colones enteros para CRC).
  • · stock: null = no controlás stock de ese ítem.
  • · variantes es opcional y plana (presentaciones con precio propio).
  • · descripcion, imagen_url y categoria son 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-Key dos veces → mismo pedido_id, sin duplicar (ante timeouts reintentamos hasta 5 veces, espaciadas unos 10 minutos).
  • · 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álido

Checklist de implementación

  • GET /catalogo con paginación.
  • 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) y `per_page` (entero, máximo 200, default 100). NUNCA devuelvas más de `per_page` ítems en una página: Guaba rechaza la página entera.
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, máx 120 chars
      "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 →