Skip to content

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.

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.

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.

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.

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.

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.

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.

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 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 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