Webhook events
Oclavex delivers events to your HTTPS endpoints so your billing, CRM and automation stay in sync without polling. The contract follows two open standards: the payload is a CloudEvents 1.0 envelope, and delivery is signed per Standard Webhooks.
Subscribing
Section titled “Subscribing”Create a subscription from the portal’s Webhooks page or the admin API:
POST /api/v1/webhooks/admin/subscriptions— register an endpoint URL, the event types you want, an optional filter, and a signing secret. The subscription starts Pending verification.GET /api/v1/webhooks/admin/subscriptions·GET | PATCH | DELETE …/{id}— manage them.POST …/{id}/verify— run the registration challenge and, on success, move the subscription to Active.POST …/{id}/test-fire— send a sample event to your endpoint.
Every event type the platform emits is discoverable, unauthenticated, at
GET /api/v1/webhooks/schemas.
Patterns and filters
Section titled “Patterns and filters”A subscription matches event types exactly (Entitlement.Activated), by prefix
wildcard (Entitlement.*), or all (*). You can narrow further by entity kind
(entitlement, monitored-asset, …), by severity (for overage events), and
by attribute equality.
Registration challenge
Section titled “Registration challenge”A subscription doesn’t verify itself — you trigger it by calling
POST …/{id}/verify, which makes Oclavex POST a one-time
Webhooks.Registration.Challenge to your URL. Respond 2xx with a JSON body
echoing the challenge — {"challenge": "<data.challenge>"} — and a
webhook-signature response header signed with your shared secret over
{webhook-id}.{webhook-timestamp}.{your response body}. The subscription becomes
Active only once both the echoed body and the signature verify — this proves
you own the endpoint and hold the secret. Subscriptions move through PendingVerification → Active, and can
later be Disabled or Suspended. The receiving side, with code, is in
Receiving webhooks.
The envelope
Section titled “The envelope”Every delivery is a CloudEvents 1.0 JSON document:
{ "specversion": "1.0", "id": "01J…", "source": "urn:oclavex:tenant:…", "type": "Entitlement.Activated", "time": "2026-06-16T10:00:00Z", "subject": "…", "datacontenttype": "application/json", "tenantid": "…", "entitykind": "entitlement", "entityid": "…", "eventversion": 1, "data": { }}data is event-specific and non-strict — ignore fields you don’t recognise, so
new ones never break you. Test deliveries (sent from the portal or the test-fire
endpoint) carry "_test": true in data; gate on it so test events never touch
real state.
Verifying a delivery
Section titled “Verifying a delivery”Each request carries the Standard Webhooks headers:
| Header | Meaning |
|---|---|
webhook-id |
The envelope id |
webhook-timestamp |
RFC 3339 UTC send time |
webhook-signature |
v1, + base64 HMAC-SHA256 |
webhook-event-type |
e.g. Entitlement.Activated |
webhook-delivery-attempt |
Retry counter |
webhook-tenant |
The emitting tenant id |
webhook-subscription |
The subscription id the delivery belongs to |
webhook-callback-token |
Optional short-lived JWT (see below) |
Recompute the signature over {webhook-id}.{webhook-timestamp}.{raw-body} and
compare in constant time:
signature = "v1," + base64( HMAC_SHA256(secret, id + "." + timestamp + "." + body) )Reject anything that doesn’t match, or whose timestamp is too old to trust.
Secret rotation
Section titled “Secret rotation”During a rotation there is an overlap window: Oclavex sends both signatures,
space-separated, in webhook-signature. Accept the delivery if either verifies,
so you can rotate keys with no dropped events.
Calling back
Section titled “Calling back”If your subscription requests callback scopes, each delivery includes a short-lived
JWT in webhook-callback-token. Use it as a bearer token to call straight back into
the admin API with exactly those scopes — no separate OAuth round-trip. Only
read / create / update / write verbs are allowed; delete and wildcards are not.
Delivery and retries
Section titled “Delivery and retries”Delivery is at-least-once — make handling idempotent by deduplicating on webhook-id. Failed
deliveries (5xx, 408, 429, timeout, DNS) retry on this schedule: up to
10 attempts within a 2-hour budget, exponential backoff with full jitter
from a 2 s base capped at 15 min, and a 30 s per-attempt timeout —
size your endpoint’s processing accordingly. Once retries are exhausted the
delivery is dead-lettered — recorded as failed, kept for inspection, and
requeueable from the portal with a fresh budget — and every attempt is audited.
Event catalog
Section titled “Event catalog”| Event type | Fires when |
|---|---|
Entitlement.Activated |
An entitlement transitions to Active |
Entitlement.Expiring |
An entitlement is approaching expiry |
Entitlement.TrialExpiring |
A trial is approaching its end |
Entitlement.Renewed |
A subscription period renews |
Entitlement.Cancelled |
An entitlement is canceled |
Entitlement.PlanApplied |
A plan is applied or changed |
Entitlement.PlanChangeRequested |
A customer’s plan-change request awaits approval — the payload’s requestId lets you approve programmatically, e.g. once a payment succeeds |
Entitlement.PlanChangeReviewed |
A plan-change request is decided — approved (including auto-approval) or rejected |
Feedback.Submitted |
An end user submits feedback — a feature request, a problem report or a general comment |
Feedback.Reviewed |
You decide on a feedback item — accepted, declined or marked as a duplicate |
MeteredUsage.OverageStatusChanged |
A monitored asset crosses a quota tier (Normal / Warning / Critical / OverUsage) |
CreditBalance.StatusChanged |
A prepaid credit balance crosses a threshold — e.g. running low or depleted — so you can prompt a top-up before work is blocked |
DataSubjectRequest.Filed · .Approved · .Rejected · .Completed |
A data-subject request changes state |
User.Erased |
A user is erased |
Webhooks.Registration.Challenge |
Sent once, to verify a new subscription |