The Outbound Webhooks Release: Ed25519, Endpoint Auth, FIFO, Polling, Versioning and More
The biggest update to sending webhooks since we launched it: Ed25519 signatures with dual-signing during rotation, Basic and OAuth2 endpoint auth, FIFO ordering, polling endpoints, event versioning, SQS/SNS/EventBridge/Pub/Sub/S3 endpoints, smarter handling of dead receivers, and a catalog, usage and tracing story to match.
Sending Webhooks Is a Product, Not a Feature
When you send webhooks to your own customers, every one of them has a different receiver. One wants to verify with a public key instead of a shared secret. One sits behind an OAuth2 gateway. One needs events strictly in order. One can't accept inbound traffic at all. One wants the events in an SQS queue, not at a URL. And all of them break when you change a payload.
This release covers those cases. It's a long list, so each item below is short, says which plan it needs, and links to the docs page with the details.
Signing and Security
Ed25519 signatures. An endpoint can now use the Standard Webhooks v1a scheme instead of HMAC. Create it with "signatureScheme": "ed25519" (or pick Ed25519 (v1a) in the dashboard). Hookbase generates the key pair, keeps the private key encrypted, and only ever returns the whpk_ public key. Your customers can publish or share that key freely, because it can verify signatures but not create them. All plans. Ed25519 signatures →
Dual-signing during rotation. Rotating a secret used to cut receivers over at once. Now, for the grace period you choose (default one hour, up to 24 hours), every delivery carries both signatures in webhook-signature: v1,<old> v1,<new> for HMAC, or v1a,<old> v1a,<new> for Ed25519. A receiver holding either secret keeps verifying while you roll the new one out. All plans. Rotate endpoint secret →
Endpoint authentication. Some receivers sit behind a gateway that wants credentials before it looks at a signature. An HTTP endpoint can now carry an authentication setting, sent as the Authorization header on top of the signature: HTTP Basic, or OAuth2 client credentials, where Hookbase fetches an access token from your token URL, caches it until shortly before it expires, and drops the cached token when the endpoint answers 401 so the next request gets a fresh one. Passwords and client secrets are stored encrypted and never returned. Recorded attempts redact the Authorization header. It's set through the API for now:
curl -X PATCH https://api.hookbase.app/api/organizations/$ORG_ID/webhook-endpoints/$ENDPOINT_ID \
-H "Authorization: Bearer $HOOKBASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"authentication": {
"type": "oauth2_client_credentials",
"tokenUrl": "https://auth.example.com/oauth/token",
"clientId": "hookbase",
"clientSecret": "your-client-secret",
"scope": "webhooks:write"
}
}'
Setting or changing it needs Starter or above. Removing it ("authentication": null) works on every plan. Endpoints API →
Delivery
FIFO ordering. Turn on FIFO ordering for an endpoint and its messages go out one at a time, in the order your sends were accepted. A failing message holds the line while it retries, then moves to the DLQ, and the queue carries on. The endpoint page shows queue depth, lag, and why the head is blocked. Use it for state machines, ledgers and sync feeds; leave it off when messages are independent, because one request in flight at a time is slower. Pro and Business. FIFO ordering →
Polling endpoints. For consumers that can't accept inbound HTTP (behind a firewall, batch jobs, on-prem software), create an endpoint with "endpointType": "polling". The consumer calls GET /v1/poll/<endpoint id> with a whpoll_ token and a cursor. Passing the cursor back acknowledges everything up to it, messages are still signed exactly like webhooks, and each endpoint allows 120 polls a minute. Starter and above. Polling endpoints →
Queues, buses and buckets. An endpoint doesn't have to be a URL anymore. It can publish each message to Amazon SQS, SNS, EventBridge, Google Cloud Pub/Sub, or S3 (including S3-compatible stores such as Cloudflare R2). Subscriptions, filters, transforms, retries, the DLQ and replays work the same as for HTTPS endpoints. Starter and above. Queues, buses and buckets →
Dead receivers get handled. Three changes to how Hookbase treats an endpoint that isn't coming back:
- A
410 Goneresponse now disables the endpoint immediately. The message moves to the DLQ with reasonendpoint_gone, and the application's operational webhooks are told. - An endpoint that has failed every delivery for five days, and has failed again in the last 24 hours, is disabled by an hourly sweep, and the organization's owners and admins get an email.
- On a
429or503, Hookbase readsRetry-Afterand never retries sooner than it asks, capped at one hour.
All plans. DLQ reasons → · Retry behavior →
Payloads
Per-subscription filters and transforms. A subscription's filterExpression is a JSONata expression evaluated against the payload at send time (order.total > 100, $labels.region = "eu"), and a subscription's transformId reshapes the payload for that endpoint before it is signed. An expression that throws or runs past one second counts as no match; a transform that throws sends the message to the DLQ as transform_failed instead of retrying it. Filter expressions → · Subscription transforms →
Event versioning. Change an event type's payload without breaking receivers. Add a version with its own schema and a JSONata downgrade expression, pin each subscription to a version (or latest), and keep sending only the newest shape. Hookbase converts each delivery down to the subscription's version and labels it with x-hookbase-event-version. Starter and above to add versions or pin to one; existing versions keep working after a downgrade. Event versioning →
Opt-in schema validation on send. Switch on Reject payloads that don't match the schema for an event type, and a send whose payload doesn't match its JSON Schema fails with 422 and a list of what's wrong, before anything is delivered:
{
"error": "Schema validation failed",
"message": "...",
"errors": [{ "path": "/orderId", "message": "..." }]
}
It's off by default, so existing event types behave as before. All plans. Event types API →
For Your Integrators
AsyncAPI, OpenAPI and TypeScript export. Download your event catalog as an AsyncAPI 3.0 or OpenAPI 3.1 document, or as a .d.ts file with one type per event type and a discriminated union. The output is deterministic, so you can commit it and diff it in CI. If you publish a public catalog, the same exports are available from it without authentication. All plans. Event catalog export →
Portal branding. The hosted customer portal can carry your logo, colours, theme, display name and support links, and it ships in six languages (en, es, fr, de, ja, pt-BR). Branding and languages are Starter and above; serving the portal from your own domain is Pro and above. Portal branding →
Usage export for billing pass-through. If you charge your customers for webhook volume, Outbound → Usage (or GET /webhook-applications/usage) reports events sent, messages, attempts, successful deliveries, failures and bytes per application, by UTC day or month, as JSON or CSV. The numbers outlive message retention, and meta.closedThrough tells you when a month is final. All plans. Usage export →
Observability
OpenTelemetry tracing for outbound. Send a W3C traceparent header with your send call and the trace continues through Hookbase: a hookbase.outbound.send span, a hookbase.outbound.delivery span per attempt (retries included), and a traceparent on every request to the receiver, so an instrumented endpoint joins the same trace. The message detail page shows the trace ID. The header goes out on every plan; exporting the spans to Grafana, Honeycomb, New Relic, Axiom or any OTLP backend uses Observability Export, which is Pro and above. Tracing →
Getting Started Got Shorter
One-click quickstart. Open Outbound Webhooks and click Send your first webhook. Hookbase creates a demo application, an order.created event type, an endpoint pointed at a request bin, and a subscription, then sends one real, signed event through the normal pipeline and shows you the request as it arrived. Running it again reuses everything instead of creating duplicates. From there, point the endpoint at your own URL or copy the ready-made curl command. Outbound getting started →
Try It
Most of this is opt-in per endpoint, subscription or event type, so nothing changes for your existing receivers until you turn it on. Two things apply everywhere: deliveries now include a traceparent header, and 410 Gone disables an endpoint. If you're new to sending webhooks, start with the quickstart. If you already send them, pick a Signature scheme when you create your next endpoint, or open an existing endpoint's edit dialog to switch on FIFO ordering.