Send your first event in 5 minutes
The fastest path is: create a workspace, create a source, point your producer at the ingest URL, and watch the event arrive in the dashboard.
Axel docs are split into four lanes: get started, primitives, operate, and reference. Each page is short by design — if a page can't be skimmed in 60 seconds, it's split into smaller ones.
The fastest path is: create a workspace, create a source, point your producer at the ingest URL, and watch the event arrive in the dashboard.
Webhook sources accept events. Routes evaluate declarative, eval-free filters and transforms. Destinations are where records land. Stable event IDs let receivers deduplicate retries.
Everything you need to know when something is on fire: replays, failed deliveries, transform errors, and how to tune queue shards as you grow.
Canonical event payload, queue message shapes, retry policy defaults, and ClickHouse schemas for log queries.
CLI or curl. Accept an event, store its payload, and send it to a destination.
curl -X POST https://app.axelapp.ai/api/v1/sources \
-H "Authorization: Bearer $AXEL_API_KEY" \
-d '{ "name": "my-webhook" }'The response includes a per-source ingest token (secret_token), shown only once. Store it as a secret in your producer's environment.
POST https://ingest.axelapp.ai/in/{source_id}
x-axel-token: <token>
content-type: application/json
{ "type": "order.created", "id": "ord_123", ... }The endpoint returns 202 on acceptance. Your event is durable in R2 before the response.
Open app.axelapp.ai: Overview shows the event count tick up, Usage shows the source-level breakdown, and Deliveries shows any failed attempts.
Every event you accept is queryable in ClickHouse for 30 days, including headers and query params. Raw payloads are kept in R2 for 30 days.
Welcome back. Here's what's happening across Northwind.
evt_01HZQ8R7XKHTTP 503 from warehouse-webhook · retry 2/12evt_01HZQ7N4WTTLS handshake timeout · destination pausedevt_01HZQ6M2VRReplay batch resolved · 11 deliveriesevt_01HZQ5L1UQSchema guard rejected · depth > 24Prefer the CLI? Create a source in the dashboard, then axel auth login and axel listen --source <source_id> --forward-to <url>, or point your webhook producer at the ingest URL shown in the dashboard.
Use the generic source for producers that can POST JSON, form data, or bytes.
Create a source with curl or the dashboard, then POST to https://ingest.axelapp.ai/in/{source_id} with the x-axel-token header you received. No signature required.
curl -X POST https://ingest.axelapp.ai/in/src_01H... \
-H "x-axel-token: $AXEL_SOURCE_TOKEN" \
-H "content-type: application/json" \
-d '{ "type": "order.created", "id": "ord_123", ... }'You can also bring a custom HMAC secret and Axel will verify an X-Axel-Signature: t=<unix>,v1=<hex> header (timestamped, 5-minute tolerance) using constant-time comparison.
Sources are the entry points. Each source has its own token (or signing secret), rate limit, body size cap, and nesting depth guard.
{
"event_id": "evt_01HZQ8R7XK",
"source": "stripe.webhooks",
"event": "invoice.paid",
"customer": "cus_PqJ8XKr",
"amount": 14250,
"currency": "usd",
"routes": ["payments.live"]
}Routes decide where events go and what they look like. Rules are data, not code.
payments.live matches invoice.paid etc.).JSONB column.stripe.webhooksmongo://ordersgithub.webhookss3://raw-ledgershopify.webhookspostgres://billingpartner.webhookhttps://hooks.exampleDeliver with retries to the systems you already run.
{date}, {event_id}). Cloudflare R2: write JSON objects under a configurable key prefix.Axel provides at-least-once delivery to external destinations. Delivery attempts use a stable key of the form workspace:event:route:destination, and retries reuse that key. A signed HTTP receiver should deduplicate on X-Axel-Event-Id because a process can fail after the destination accepts a request but before Axel records the acknowledgement. Replays use a new event ID and are intentionally delivered again.
Replay an accepted event while its original payload is retained.
From the dashboard or CLI:
# Dashboard: click any event → Replay
# CLI (exact bytes to your laptop)
axel replay evt_01HZQ8R7XK --forward-to http://localhost:3000/webhook--keep-signature to forward them anyway.Every delivery attempt is recorded. Permanent failures (after all retries) land in the Inbox with one-click Retry and Mute. No SQL, no digging through DLQ tables.
The event detail page shows the receipt, the stored payload, and a delivery history of every HTTP attempt with its status, latency, and the failure reason (TLS error, 503, timeout, etc.).
If a declarative filter excludes an event it is dropped; if a transform or filter errors at runtime the event is dead-lettered and shows up in the Inbox. Oversized or too-deep payloads are rejected at ingest with a 413 before they are ever stored or routed.
Every stored event carries Axel metadata and a pointer to the raw body in R2 (the body is referenced by r2_key rather than inlined). Example shape:
{
"event_id": "evt_01HZQ8R7XK",
"workspace_id": "ws_...",
"source_id": "src_...",
"received_at": "2026-06-16T14:22:09.123Z",
"r2_key": "...",
"size_bytes": 1024,
"headers": { "x-my-signature": "..." },
"query": {}
}Up to 12 delivery attempts with exponential backoff. Retries are included in the inbound event price. The retry policy is fixed and not per-source configurable.
All receipt, routing, and delivery rows are queryable in ClickHouse for 30 days (headers, query params, and failure reasons; raw payloads live in R2). The dashboard search and the axel CLI use these tables. Export or run your own queries from the usage / deliveries views.
Docs are intentionally concise. If something is missing or unclear, email the founders — we read every message and improve the site from real questions.