API: webhooks
Bricqs delivers outbound events to your backend. Reward issuance, challenge completion, contest results, drop-off triggers. Each delivery is HMAC-signed and retried with exponential backoff.
Key takeaways
Quick read- Subscribe per event_type and per tenant. Each subscription has its own URL and secret.
- Every payload is HMAC-SHA256 signed with the subscription secret. Always verify before acting.
- Retry policy: 5 attempts with exponential backoff over ~30 minutes. After that, the delivery is marked failed.
- Idempotency on your side: dedupe by event_id. The same event can be redelivered.
- Sub-1s response is required. Do work asynchronously after a 200; webhooks should not wait on your CRM.
Subscribe
Create a webhook subscription
curl -X POST https://api.bricqs.co/api/v1/admin/webhooks \
-H "Authorization: Bearer bq_live_admin_..." \
-d '{
"url": "https://api.your-app.com/bricqs/webhooks",
"events": [
"challenge.completed",
"reward.issued",
"contest.completed",
"challenge.drop_off_at_72h"
],
"secret": "wh_secret_..."
}'Generate the secret yourself or let Bricqs return one. Either way, store it server-side; never ship it to the browser.
Payload
Shape and headers
Headers:
X-Bricqs-Signature: sha256=hex(hmac_sha256(secret, raw_body))
X-Bricqs-Event: challenge.completed
X-Bricqs-Delivery: d_01HW...
X-Bricqs-Tenant: tenant_brand_xyz
Content-Type: application/json
Body:
{
"id": "evt_01HW...",
"type": "challenge.completed",
"occurred_at": "2026-04-30T10:42:00Z",
"tenant_id": "tenant_brand_xyz",
"data": {
"challenge_id": "fitness_30day_apr",
"participant_id": "p_8a3f",
"completed_at": "2026-04-30T10:42:00Z",
"reward": { "id": "rwd_42", "type": "voucher", "value": 500 }
}
}Verify
Always check the signature
import crypto from "crypto";
export async function POST(req: Request) {
const raw = await req.text();
const signature = req.headers.get("x-bricqs-signature") ?? "";
const expected =
"sha256=" +
crypto
.createHmac("sha256", process.env.BRICQS_WEBHOOK_SECRET!)
.update(raw)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return new Response("Invalid signature", { status: 401 });
}
const event = JSON.parse(raw);
// 1. Acknowledge fast (sub-1s)
await queueForProcessing(event);
// 2. Return 200 immediately
return new Response("ok");
}Use timingSafeEqual to avoid timing-attack leaks. Acknowledge inside 1 second; do real work in a queue.
Retries
What happens when you fail
Bricqs retries any non-2xx response.
Schedule:
attempt 1 immediate
attempt 2 ~1 minute later
attempt 3 ~3 minutes later
attempt 4 ~10 minutes later
attempt 5 ~30 minutes later
give up mark delivery failed, expose in dashboard
Retry-Safe headers:
X-Bricqs-Delivery is unique per delivery attempt
event.id is the same across retries; dedupe by event.id
Manual replay:
POST /admin/webhooks/:id/redeliver/:event_id
Useful when fixing a downstream bug.Event types
The most-used events
Lifecycle:
challenge.completed
challenge.drop_off_at_72h
challenge.drop_off_at_168h
contest.completed
contest.disqualification
Rewards:
reward.issued
reward.claimed
reward.expiring_in_7_days
Progression:
tier.upgraded
badge.earned
streak.milestone_hit
Points:
points.granted (only for direct grants; earn-rule grants are silent)
points.expired
The full list is in the dashboard. New events ship monthly; subscriptions
default to ignore unknown types so you do not break on rollouts.Common mistakes
What goes wrong
Verifying the parsed JSON instead of the raw body. Signature fails on every payload.
Read the raw body once, verify against it, then parse. Frameworks that auto-parse JSON break this; opt out for the webhook route.
Doing CRM and email work inside the webhook handler. Timeouts cause retries.
Queue the work, return 200, process async. Bricqs retries non-2xx; you want every delivery to succeed first try.
Using a single secret across many subscriptions.
One secret per subscription. Rotation becomes per-subscription, not all-or-nothing.
Ignoring redelivered events. CRM gets the same upgrade email twice.
Dedupe by event.id in your handler. The event.id is stable across redelivery.
Developer FAQ
Common questions when integrating gamification with Bricqs.
Ready to ship?
Wire it up with the Bricqs SDK or API
Headless SDK for React UIs, REST API for any backend. Same engine behind both.
