Accept your first event
Create a workspace and a source, then send an event to its ingest URL.
Start on Axel Cloud to use the managed service. These guides cover sources, routing, delivery, and recovery. You can also self-host the Apache-2.0 application.
The small install runs Postgres, the dashboard, and delivery in Docker, with Cloudflare Workers, Queues, and R2 for ingest. You maintain the host, backups, and upgrades. Add ClickHouse for searchable event history and usage charts. There is no Axel license fee. Provider charges depend on your traffic and setup.
Create a workspace and a source, then send an event to its ingest URL.
Sources accept events. Routes filter and transform them. Destinations receive the result. Retries keep the same event ID so receivers can deduplicate them.
Inspect failures, fix route errors, and replay retained events. Self-host operators also manage queue capacity and retention.
Event metadata, retry defaults, storage, and response codes.
Create an Axel Cloud workspace and a workspace API key with write access. Set AXEL_API_KEY in your shell before running the example. For self-hosting, use your dashboard and ingest URLs in place of the Cloud URLs below.
curl -X POST https://app.axelapp.ai/api/v1/sources \
-H "Authorization: Bearer $AXEL_API_KEY" \
-H "content-type: application/json" \
-d '{ "name": "my-webhook" }'The response includes the source ID, ingest URL, and secret_token. Axel shows the token only once. Set AXEL_INGEST_URL to the returned ingest URL and AXEL_SOURCE_TOKEN to the token before the next command.
curl -X POST "$AXEL_INGEST_URL" \
-H "x-axel-token: $AXEL_SOURCE_TOKEN" \
-H "content-type: application/json" \
-d '{ "type": "order.created", "id": "ord_123" }' Axel returns 202 after storing the payload and queueing it for routing. This confirms acceptance, not destination delivery.
Open app.axelapp.ai. Check Overview for accepted events and Usage for traffic by source. To deliver events onward, create a destination and an active route from this source, then send another event and check Deliveries.
Axel Cloud keeps searchable event history for 30 days. Raw payloads expire after 30 days by default, or sooner if you configure shorter retention.
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 > 24To use the CLI, follow the installation guide. Create a source in the dashboard, then run 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. For self-hosting, sign in with axel auth login --api-base https://axel.example.com, using your own dashboard URL.
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 with a five-minute timestamp tolerance. See webhook authentication for sender setup and URL authentication.
Each source has its own authentication, rate limit, body size limit, and nesting depth limit.
{
"event_id": "evt_01HZQ8R7XK",
"source": "stripe.webhooks",
"event": "invoice.paid",
"customer": "cus_PqJ8XKr",
"amount": 14250,
"currency": "usd",
"routes": ["payments.live"]
}Routes choose destinations and define which fields each destination receives.
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.
Use the dashboard to replay through your routes. Use the CLI to send stored bytes directly to a local handler:
# Dashboard: click any event → Replay
# CLI (exact bytes to your laptop)
axel replay evt_01HZQ8R7XK --forward-to http://localhost:3000/webhook --keep-signature preserves only headers still present in the retrieved metadata; it cannot restore headers removed at ingestion or make an expired signature valid.Terminal failures appear in the Inbox after retries are exhausted or the destination returns a non-retryable error. Open the failure to inspect its history, retry after a fix, or mute it.
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.
ClickHouse stores receipt, routing, and delivery metadata for 30 days. The dashboard and CLI use it for event search and delivery history. Raw payloads stay in R2. The small self-host profile omits ClickHouse, so analytics-backed search and usage views are unavailable. Its raw payload retention is fixed at 30 days; see the self-hosting guide before choosing a profile.
Ask setup and usage questions in GitHub Discussions. Contact Axel Cloud support privately for account or billing questions.