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 clean records land. Idempotency keys make every fan-out safe.
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. Under two minutes to a durable, searchable event.
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 https://app.axelapp.ai
# → Overview shows the event count tick up
# → Usage tab shows the source-level breakdown
# → Deliveries shows any failed attemptsEvery event you accept is queryable in ClickHouse for 30 days, including headers and query params. Raw payloads are kept in R2 for 30 days.
Prefer 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.
Any producer that can POST JSON (or form / bytes) is supported.
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.
Routes decide where events go and what they look like. Rules are data, not code.
payments.live matches invoice.paid etc.).JSONB column.Deliver exactly once (with retries) to the systems you already run.
{date}, {event_id}). Cloudflare R2: write JSON objects under a configurable key prefix.Every delivery attempt uses a stable key of the form workspace:event:route:destination. Retries reuse the same key, so re-attempting a failed delivery never produces a duplicate even across queue shards. (Replays are issued under a distinct key so they are delivered, not deduped against the original.)
Reproduce any event you ever received, exactly.
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 never billable. 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.