Bricqs
Documentation

Webhooks

Bricqs delivers progression events (badges, tiers, points, streaks, milestones, challenges, contests, referrals, rewards) and engagement activity events to HTTPS endpoints you register. Every delivery is HMAC-signed, retried on failure, logged, and manually replayable. The machine-readable list of subscribable events and their payload schemas is published at https://api.bricqs.co/.well-known/events.json.

Auth
All endpoints require an X-API-Key header. Production keys are prefixed bq_live_; sandbox keys bq_test_.
Scopes
Admin scope All destination-management endpoints require admin auth: a dashboard session or an API key carrying the admin or gamify:admin scope. Deliveries themselves are outbound; your consumer needs no Bricqs credential, only the destination secret for signature verification.
Base URLhttps://api.bricqs.co/api/v1/gamify

Endpoint inventory

MethodEndpointPurpose
GET/gamify/webhooksList destinations.
POST/gamify/webhooksCreate a destination. Returns the signing secret.
GET/gamify/webhooks/{destination_id}Read one destination.
PATCH/gamify/webhooks/{destination_id}Update url, events, retries, timeout, active state. The URL is re-validated against SSRF rules.
DELETE/gamify/webhooks/{destination_id}Delete a destination.
POST/gamify/webhooks/{destination_id}/testSend a signed test delivery (X-Bricqs-Event: ping).
GET/gamify/webhooks/{destination_id}/logsDelivery history (status, attempts, response codes).
POST/gamify/webhooks/{destination_id}/deliveries/{log_id}/replayReplay one delivery.
POST/gamify/webhooks/{destination_id}/replayBulk replay; returns a job id.
GET/gamify/webhooks/{destination_id}/replay-jobs/{job_id}Bulk replay job status.
POST/api/v1/gamify/webhooks
Admin scope

Register an HTTPS endpoint for event delivery. The response includes the generated signing secret: store it server-side. There is no secret-rotation endpoint; to rotate, delete the destination and create a new one.

Request body
FieldTypeDescription
namerequiredstrDisplay name, 1 to 100 characters. Names prefixed [system] are reserved and rejected.
urlrequiredstrHTTPS endpoint URL. Validated against SSRF rules (no private ranges).
eventsrequiredlist[str]Event types to subscribe to. Every entry must exist in the published catalog (events.json); unknown names are rejected.
engagement_idOptional[str]Scope delivery to one engagement. Omit for all.
custom_headersdictExtra headers added to every delivery (for your own auth). Default: {}
retry_countintRetries after the initial attempt, 0 to 5. Default: 3
timeout_secondsintPer-attempt response timeout, 3 to 60. Default: 10
# Standard bq_live_... key carrying the admin (or gamify:admin) scope.
curl -X POST https://api.bricqs.co/api/v1/gamify/webhooks \
  -H "X-API-Key: bq_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "crm-progression-sink",
    "url": "https://api.your-app.com/bricqs/webhooks",
    "events": ["badge.earned.v1", "tier.changed.v1", "reward.claimed.v1"]
  }'
Errors
StatusCodeWhen it fires
401AUTH_UNAUTHENTICATEDNo admin credential supplied.
403AUTH_FORBIDDENCredential lacks the admin / gamify:admin scope.
422VALIDATION_INVALID_VALUEUnknown event name, non-HTTPS or SSRF-blocked URL, reserved [system] name prefix, or out-of-range retry/timeout.

Delivery contract

Every delivery is an HTTPS POST to your destination URL with these headers and a canonical JSON envelope. The same envelope (same id) also appears on the participant’s live SSE stream, so the two transports are correlatable.

Delivery headers
FieldTypeDescription
X-Bricqs-Signaturestrt={unix_timestamp},v1={hex}. HMAC-SHA256 over the string "{timestamp}.{raw_body}" with the destination secret.
X-Bricqs-EventstrEvent name (ping for test deliveries).
X-Bricqs-TimestampstrUnix timestamp used in the signature.
X-Bricqs-Delivery-IdstrStable across retries of the same delivery. Use this as your dedupe key.
Envelope body
FieldTypeDescription
idstrevt_-prefixed event id. Equals X-Bricqs-Delivery-Id.
eventstrEvent name, exactly as in the catalog.
timestampstrISO 8601 emission time (UTC).
tenant_idstrYour tenant id.
engagement_idOptional[str]Set when the event is engagement-scoped.
participant_idOptional[str]The affected participant.
subjectOptional[object]What the event is about: {kind, id, name}, for example the badge or reward. Always present; null when unknown. When present, kind is always set and id or name can be null.
originOptional[object]What caused the event: {kind, id, name}, for example an engagement, a rule or a flow. Always present; null when unknown. Same shape as subject.
datadictEvent payload, exactly as emitted. Schema per event in events.json. Never merged with envelope fields.

The envelope schema and the full list of subject and origin kinds are published in events.json under envelope. New envelope keys and new kinds may be added; existing keys and kinds are never renamed or removed. Ignore keys you do not know, and treat an unknown kind as other.

Retry schedule

A delivery succeeds only on an HTTP 2xx response within timeout_seconds. Anything else (timeout, 4xx, 5xx) schedules a retry with this backoff:

attempt 1   immediate
attempt 2   +30 seconds
attempt 3   +5 minutes
attempt 4   +30 minutes
(total attempts = retry_count + 1; retry_count default 3, max 5)

After the final attempt the delivery is EXHAUSTED (terminal).
Recovery is manual: the replay endpoints above.

Subscribable events

The catalog at https://api.bricqs.co/.well-known/events.json is the contract: 33 events with JSON payload schemas, including participant.joined.v1 (a person joined, once; the payload says how, and whether the profile was new or already existed) and participant.anniversary.v1 (a yearly date a member gave you came round: the field, the day and the year). Versioned progression events (badge.earned.v1, tier.changed.v1, points.awarded.v1, streak.continued.v1, challenge.completed.v1, contest.prize_allocated.v1, referral.converted.v1, reward.claimed.v1, reward.points_expired.v1, and more) plus legacy engagement activity events (activity.completed, quiz.completed, ...).

Each catalog entry carries a status: active events fire today; deferred events (currently game.completed) are subscribable and schema-published but have no emitter yet. Do not block an integration waiting on a deferred event. A won reward ends with exactly one of reward.redeemed.v1, reward.expired.v1 or reward.revoked.v1; none of them carries the code. points.expiring.v1 fires a week before a member's points expire, once per member per expiry day.

Non-guarantees

  • No ordering. Deliveries are not ordered, within or across participants. Retries make out-of-order arrival normal; process idempotently and order on the envelope timestamp if you need a sequence.
  • Old events cannot be replayed forever. A retry or replay sends the same envelope as the first delivery. Once the event is past its retention window (at least 90 days) it can no longer be sent; the delivery is closed as payload expired instead of being sent with an empty data.
  • No secret rotation endpoint. Rotate by deleting and recreating the destination (two destinations can point at the same URL during the cutover).
  • Exhausted is terminal. Nothing re-sends automatically after the retry schedule; use the replay endpoints.

Next steps