Skip to main content
Webhooks are how Cativa notifies you when something happens, instead of you polling the API every minute. This is the conceptual view. For the step-by-step on subscribing, signature verification, and retries, see Subscribing and verifying webhooks.

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 with Content-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.
Register the URL and the event types you want to listen to in the Cativa dashboard, under Console > Webhooks. Each listener gets its own 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’s secret. 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:
  • t is the Unix timestamp (seconds) of the moment of delivery.
  • v1 is the HMAC-SHA256 (hex) over the string "<t>.<rawBody>", using the listener’s secret as the key.
Full verification examples (Node, Python, Go, C#) live in Subscribing and verifying webhooks.
Compute the HMAC over the raw body (the exact string received), not over re-serialized JSON. Re-serializing changes whitespace and key order, which invalidates the signature.

Redelivery with backoff

If your endpoint doesn’t reply with 2xx, Cativa retries on this curve:
That’s 6 retries after the initial attempt, 7 deliveries in total, covering roughly 33 hours. Cativa honors the 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

Unlike many APIs, the payload does not use a generic envelope ({id, type, data}). Each event has its own shape in PascalCase, with CustomerId at the top level for multi-tenant routing.
Example (payload for user_received_badge):
Common envelope fields across events:

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.
The full list of available events (including 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

Do not run slow operations (HTTP calls, heavy queries, report generation) inside the webhook handler. Return 2xx as fast as possible and process in the background.The correct pattern: the endpoint enqueues the event into your own queue and responds 200. A worker on your side processes it later, calmly.

Idempotency on your side

Since delivery is at-least-once, you need to detect duplicates. Use the X-Cativa-Execution-Id header (always sent and stable across retries of the same event) as a deduplication key:
Persisting the execution identifier in the same transaction as the business logic guarantees that either everything happened or nothing happened, with no chance of double-processing.

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.