Push vs poll: why webhooks
Without webhooks, to know whether a user received a badge you’d have to hit the API repeatedly and diff the result, a pattern called polling. It wastes requests, delays your reaction, and doesn’t scale. With webhooks, Cativa pushes the event to you the instant it happens. You react immediately and only when there’s actually something new.How it works
When something relevant happens in a tenant (user receives a badge, post is created, payment confirmed), Cativa builds the event payload and POSTs it to your public endpoint withContent-Type: application/json.
1
An action happens in the tenant
A user signs up, earns a badge, publishes a post, or completes a payment.
2
Cativa builds the payload
The platform enriches the event with the data you need to process it in isolation (user, badge, payment, and so on).
3
Cativa signs and POSTs to your endpoint
The delivery carries the
X-Cativa-Signature, X-Cativa-Execution-Id, and X-Cativa-Automation-Id headers.4
Your endpoint verifies, enqueues, and returns 200
You check the HMAC signature, deduplicate by
X-Cativa-Execution-Id, enqueue the processing, and return 2xx fast.secret (format whsec_ plus 64 hex characters), used to sign every delivery from that listener.
Headers on every delivery
Delivery guarantees
At-least-once
Each event is delivered at least once. Always use
X-Cativa-Execution-Id for idempotency on your side, avoiding double-processing in case of duplicates.Order NOT guaranteed
Events can arrive out of the order in which they happened. Don’t write code that assumes
user_created arrives before user_received_badge, they can swap.Why no ordering
Cativa delivers events in parallel to reach your endpoint quickly. Enforcing order would cut throughput by an order of magnitude. Instead, each event payload contains all the data you need to process it in isolation.HMAC signature (X-Cativa-Signature)
Every delivery is signed with HMAC-SHA256 using the listener’ssecret. You verify the signature before processing the event, ensuring the request really came from Cativa and the body was not tampered with in transit.
The signature ships in this header:
tis the Unix timestamp (seconds) of the moment of delivery.v1is the HMAC-SHA256 (hex) over the string"<t>.<rawBody>", using the listener’ssecretas the key.
Redelivery with backoff
If your endpoint doesn’t reply with2xx, Cativa retries on this curve:
Retry-After header you return (capped at the next backoff window’s max).
When we retry vs permanent failure
If every attempt fails, the delivery is recorded internally as failed. v1 does not yet ship a Console UI to inspect failed deliveries. Contact Cativa support to investigate.
Payload format
Example (payload foruser_received_badge):
Event catalog
Cativa exposes events for the platform’s main actions. Start with the canonical event:user_received_badge
Fired when a badge is assigned to a user. Full reference page with payload and example receiver.
user_created
New user signed up.
user_joined_group
User joined a group (manual or via badge).
post_created
New post published in a group.
paywall_payment_completed
Paywall payment completed successfully.
comment_created, course_completed, lesson_completed, and user_received_private_message, with a reference page coming soon) lives in Subscribing and verifying webhooks.
Anti-pattern: processing events synchronously in the endpoint
Idempotency on your side
Since delivery is at-least-once, you need to detect duplicates. Use theX-Cativa-Execution-Id header (always sent and stable across retries of the same event) as a deduplication key:
Next steps
Subscribing and verifying webhooks
How to register a listener, verify HMAC, and handle retries, with examples in Node, Python, Go, and C#.
user_received_badge
Full reference page for an event, with a concrete example of payload and receiver.
Errors and rate limits
The same backoff curve, applied when you call the API and get
429 or 5xx.Payment Links and Subscriptions
Where the
paywall_payment_completed event comes from and what it unlocks.