AxeneAxene Docs
Tracking & Analytics

Webhooks

Receive real-time HTTP callbacks when email events occur in Axene Mailer.

Webhooks let you receive real-time notifications about email events by POSTing JSON payloads to a URL you control. Instead of polling the API, your server gets notified the moment something happens.

Setting up a webhook

Via the dashboard

  1. Go to Settings → Webhooks.
  2. Click Add Webhook.
  3. Enter your endpoint URL (must be HTTPS).
  4. Select the events you want to receive.
  5. Click Save.

Via the API

curl -X POST https://mail.axene.io/v1/webhooks \
  -H "Authorization: Bearer axm_k_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/axene-mailer",
    "events": ["email.delivered", "email.bounced"]
  }'

The create response includes the webhook's id, url, events, secret, is_active flag, and created_at. Store the secret - you need it to verify incoming requests.

List all webhooks:

curl https://mail.axene.io/v1/webhooks \
  -H "Authorization: Bearer axm_k_YOUR_API_KEY"

A webhook can be updated with PATCH /v1/webhooks/{id} (change url, events, or is_active) and removed with DELETE /v1/webhooks/{id}.

Events

A webhook subscribes to a list of event-type strings. Only events whose type is in the webhook's events array are delivered. Event types use the email.<event> form, for example email.sent, email.delivered, email.opened, email.clicked, email.bounced, email.failed.

Payload format

Each delivery is a POST with a JSON body shaped like this:

{
  "type": "email.delivered",
  "email_id": "a1b2c3d4-0000-0000-0000-000000000001",
  "data": {}
}

type is the event type, email_id is the email the event belongs to, and data carries any event-specific metadata.

Every request also carries these headers:

HeaderValue
X-Axene-EventThe event type (same as type in the body).
X-Axene-Delivery-IDThe unique ID of this delivery attempt.
X-Axene-SignatureHMAC-SHA256 signature of the body (see below).

Retry policy

A delivery is treated as successful only when your endpoint responds with a 2xx status. A non-2xx response, a timeout (30-second limit), or a connection error counts as a failure and is retried:

AttemptDelay before retry
11 minute
25 minutes
330 minutes
42 hours
58 hours

After five retries are exhausted, Axene stops retrying that delivery and sends you an in-app notification so you can fix the endpoint. Every attempt is recorded - see the webhook's deliveries below.

Inspecting deliveries

Each attempt is logged as a delivery with its status, the HTTP response code, the attempt number, and the next scheduled retry.

curl "https://mail.axene.io/v1/webhooks/{webhook_id}/deliveries?page=0&limit=20" \
  -H "Authorization: Bearer axm_k_YOUR_API_KEY"

Fetch a single attempt - including the full payload and response body - with GET /v1/webhooks/{webhook_id}/deliveries/{delivery_id}.

Verifying webhook signatures

Every webhook request includes an X-Axene-Signature header. Verify it to ensure the request came from Axene and was not tampered with.

The header value is sha256= followed by the HMAC-SHA256 hex digest of the raw request body, signed with your webhook's secret (returned when you create the webhook).

import hmac
import hashlib
 
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
import crypto from "crypto";
 
function verifySignature(body, signature, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(body).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Always verify the X-Axene-Signature header before processing webhook payloads. Without verification, an attacker could forge webhook requests to your endpoint.

Testing webhooks

Use a service like webhook.site to get a temporary URL for testing. Add it as a webhook endpoint and inspect the payloads it receives.

Click Send Test next to any webhook in the dashboard (or call POST /v1/webhooks/{webhook_id}/test) to queue a sample email.delivered delivery without sending a real email. The test delivery appears in the webhook's deliveries like any other.

On this page