Empezar en 15 minutos

Del primer request al primer webhook. Todo contra sandbox, sin tocar nada real.

0. Lo que necesitas

Una credencial hp_test_… que te da la agencia que administra tu cuenta. Apunta a una subcuenta de prueba con datos ficticios.

export HAPEE_KEY="hp_test_..."
export HAPEE_URL="https://beta.hapee.ai/api/v1"

1. Confirmar que la credencial sirve · 1 min

curl -s "$HAPEE_URL/me" -H "Authorization: Bearer $HAPEE_KEY"
{
  "key": { "id": 44, "name": "Mi ERP sandbox", "mode": "test",
           "read_only": false, "scoped_to_subaccount": true },
  "subaccount": { "id": 91, "name": "Cliente — Sandbox",
                  "timezone": "America/Santiago", "default_currency": "CLP",
                  "sandbox": true, "language": "es" },
  "limits": { "requests_per_minute": 100, "batch_max_items": 100,
              "events_retention_days": 30, "idempotency_window_hours": 24 },
  "api_version": "v1"
}

Mira que sandbox diga true. Si dice false, paras acá: esa credencial apunta a datos reales.

2. Crear un contacto · 2 min

curl -s -X POST "$HAPEE_URL/contacts/upsert" \
  -H "Authorization: Bearer $HAPEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: mi-primera-prueba-001" \
  -d '{
    "external_id": "ERP-DEMO-1",
    "email": "prueba@ejemplo.cl",
    "first_name": "Ana", "last_name": "Pérez",
    "phone": "+56912345678",
    "rut": "12.345.678-5"
  }'
{ "data": { "id": 90211, "email": "prueba@ejemplo.cl", "…": "…" },
  "created": true }

external_id es el id de tu sistema. Es lo que hace que la carga se pueda repetir sin duplicar: la segunda vez actualiza en lugar de crear.

3. Probar que la idempotencia funciona · 2 min

Corre el mismo comando otra vez, sin cambiar nada. Vas a recibir la misma respuesta y la cabecera Idempotent-Replay: true. No se creó un segundo contacto.

Ahora corre una tercera vez cambiando first_name pero dejando la misma Idempotency-Key:

{ "error": {
    "code": "idempotency_key_reusada",
    "message": "Esta Idempotency-Key ya se usó con un cuerpo distinto..."
} }

Eso es correcto: misma clave con otro cuerpo significa que la reusaste para otra cosa. Una clave por operación.

4. Registrar un webhook · 5 min

Necesitas una URL pública. Para probar en tu máquina, levanta un túnel:

ngrok http 3000     # te da algo como https://a1b2c3.ngrok-free.app
curl -s -X POST "$HAPEE_URL/webhooks/endpoints" \
  -H "Authorization: Bearer $HAPEE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://a1b2c3.ngrok-free.app/hooks",
    "description": "Pruebas locales",
    "events": ["*"]
  }'

Guarda el secret ahora. Es la única vez que se muestra completo. Después solo vas a ver whsec_…aB3d.

Receptor mínimo

const express = require('express');
const crypto = require('crypto');
const app = express();

const SECRETO = 'whsec_aB3dE...';

app.post('/hooks', express.raw({ type: 'application/json' }), (req, res) => {
  const crudo = req.body.toString('utf8');
  const cab = req.get('X-Hapee-Signature') || '';
  const { t, v1 } = Object.fromEntries(
    cab.split(',').map(p => p.split('=').map(s => s.trim())));

  const esperado = crypto.createHmac('sha256', SECRETO)
                         .update(`${t}.${crudo}`).digest('hex');
  if (esperado !== v1) return res.sendStatus(401);

  console.log('evento:', JSON.parse(crudo).type);
  res.sendStatus(200);
});

app.listen(3000);

En producción hace falta comparación en tiempo constante y una ventana de frescura. Está completo en Webhooks, con ejemplos en Node, Python, PHP y C#.

5. Disparar el primer evento · 1 min

curl -s -X POST "$HAPEE_URL/webhooks/endpoints/12/ping" \
  -H "Authorization: Bearer $HAPEE_KEY"

En tu consola: evento: webhook.test

Y ahora uno de verdad — actualiza el contacto del paso 2:

curl -s -X POST "$HAPEE_URL/contacts/upsert" \
  -H "Authorization: Bearer $HAPEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: mi-primera-prueba-002" \
  -d '{"external_id": "ERP-DEMO-1", "match_by": "external_id",
       "first_name": "Ana María"}'

En tu consola: evento: contact.updated

Los eventos se disparan por todo lo que pasa en la subcuenta, no solo por lo que haces tú. Si alguien del equipo comercial mueve un negocio a «ganado» desde la aplicación, te llega igual.

6. Si no puedes exponer una URL · 2 min

Lo mismo, pero consultando tú:

curl -s "$HAPEE_URL/events?limit=10" -H "Authorization: Bearer $HAPEE_KEY"

Guarda el next_cursor y pásalo la próxima vez. Mismos eventos, mismo cuerpo.

7. Ver qué pasó con una entrega

curl -s "$HAPEE_URL/webhooks/deliveries?endpoint_id=12" \
  -H "Authorization: Bearer $HAPEE_KEY"

Cada entrega dice su estado, cuántos intentos lleva, qué respondió tu endpoint y cuándo es el próximo intento.

Listo. ¿Y ahora?

Antes de pasar a producción, el checklist está al final de la guía de integración. Pasar a live es cambiar la credencial y la URL del webhook: nada más.