Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cativa.digital/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks are how Cativa notifies you when something happens — instead of you polling the API every minute. This page is the high-level view. For the step-by-step on subscribing, signature verification and retries, see Subscribing and verifying webhooks.

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 to your public endpoint with Content-Type: application/json.
Action in tenant


Cativa builds the payload


POST https://your-app.com/webhooks/cativa
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_ + 64 hex), used to sign every delivery from that listener.

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:
X-Cativa-Signature: t=1715177521,v1=8c1d4e2a3b5f4d8a9c6e7f0b1a2d3e4f8a9c6e7f0b1a2d3e4f8a9c6e7f0b1a2d
  • t — Unix timestamp (seconds) of the moment of delivery.
  • v1 — 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.

Retries with backoff

If your endpoint doesn’t reply with 2xx, Cativa retries on this curve:
30s  →  5min  →  30min  →  2h  →  6h  →  24h
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

StatusBehavior
2xxSuccess — no retry
408 Request TimeoutRetry
429 Too Many RequestsRetry (honors Retry-After)
5xx (500, 502, 503, 504, …)Retry
Network errors / TCP timeoutsRetry
400 Bad RequestPermanent failure — no retry
401 UnauthorizedPermanent failure — no retry
403 ForbiddenPermanent failure — no retry
404 Not FoundPermanent failure — no retry
410 GonePermanent failure — no retry
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):
{
  "CustomerId": "01HQ0...",
  "BadgeId": "01HQ4...",
  "BadgeName": "Premium",
  "User": {
    "Id": "01HQ7Z3X4Y5Z6A7B8C9D0E1F2G",
    "Email": "mary@example.com",
    "DisplayName": "Mary Smith"
  },
  "ReceivedAt": "2026-05-08T14:32:01Z"
}
Common envelope fields across events:
FieldDescription
CustomerIdID of the tenant that emitted the event. Use it to route when your endpoint receives webhooks from multiple tenants.
Event-specific fieldsEach event adds its own fields (e.g. BadgeId, BadgeName, User, ReceivedAt for user_received_badge). See each event’s page for the exact shape.

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) 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.
app.post('/webhooks/cativa', express.json(), async (req, res) => {
  // Enqueue for later processing — don't process here
  await myQueue.enqueue(req.body);
  res.status(200).send('ok');
});

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:
async function processEvent(executionId, payload) {
  const alreadyProcessed = await db.events.exists(executionId);
  if (alreadyProcessed) return;

  await db.transaction(async (tx) => {
    await applyBusinessLogic(tx, payload);
    await tx.events.insert({ id: executionId, processedAt: new Date() });
  });
}
Persisting the execution identifier in the same transaction as the business logic guarantees that either everything happened or nothing happened — no chance of double-processing.

Next steps

Subscribing and verifying webhooks

How to register a listener, verify HMAC, handle retries.

user_received_badge

Full reference page for an event — concrete example of payload and receiver.