Get started in 15 minutes
From your first request to your first webhook. All against the sandbox, without touching anything real.
0. What you need
An hp_test_… credential, provided by the agency that manages your account. It points to a test subaccount with fictitious data.
export HAPEE_KEY="hp_test_..."
export HAPEE_URL="https://beta.hapee.ai/api/v1"
1. Confirm the credential works · 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"
}
Check that sandbox says true. If it says false, stop here: that credential points to real data.
2. Create a contact · 2 min
curl -s -X POST "$HAPEE_URL/contacts/upsert" \
-H "Authorization: Bearer $HAPEE_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-first-test-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 is the id from your system. It is what makes the load repeatable without duplicating: the second time it updates instead of creating.
3. Test that idempotency works · 2 min
Run the same command again, without changing anything. You will get the same response and the Idempotent-Replay: true header. No second contact was created.
Now run it a third time, changing first_name but keeping the same Idempotency-Key:
{ "error": {
"code": "idempotency_key_reusada",
"message": "Esta Idempotency-Key ya se usó con un cuerpo distinto..."
} }
That is correct: the same key with a different body means you reused it for something else. One key per operation.
4. Register a webhook · 5 min
You need a public URL. To test on your machine, start a tunnel:
ngrok http 3000 # gives you something like 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": "Local tests",
"events": ["*"]
}'
Save the secret now. It is only shown in full once. Afterwards you will only see whsec_…aB3d.
Minimal receiver
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('event:', JSON.parse(crudo).type);
res.sendStatus(200);
});
app.listen(3000);
In production you need a constant-time comparison and a freshness window. The full version is in Webhooks, with examples in Node, Python, PHP and C#.
5. Trigger the first event · 1 min
curl -s -X POST "$HAPEE_URL/webhooks/endpoints/12/ping" \
-H "Authorization: Bearer $HAPEE_KEY"
In your console: evento: webhook.test
And now a real one — update the contact from step 2:
curl -s -X POST "$HAPEE_URL/contacts/upsert" \
-H "Authorization: Bearer $HAPEE_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-first-test-002" \
-d '{"external_id": "ERP-DEMO-1", "match_by": "external_id",
"first_name": "Ana María"}'
In your console: evento: contact.updated
Events fire for everything that happens in the subaccount, not just what you do. If someone on the sales team moves a deal to “won” from the app, you still get it.
6. If you can't expose a URL · 2 min
The same thing, but polling yourself:
curl -s "$HAPEE_URL/events?limit=10" -H "Authorization: Bearer $HAPEE_KEY"
Save the next_cursor and pass it next time. Same events, same body.
7. See what happened to a delivery
curl -s "$HAPEE_URL/webhooks/deliveries?endpoint_id=12" \
-H "Authorization: Bearer $HAPEE_KEY"
Each delivery shows its status, how many attempts it has made, what your endpoint responded and when the next attempt is.
Done. What's next?
Integrate an ERP
The complete flows, field mapping and the go-live checklist.
Webhooks
Signatures in four languages and the receiver rules.
Full reference
Every endpoint with its parameters.
Before going to production, the checklist is at the end of the integration guide. Going live means changing the credential and the webhook URL: nothing else.