Configure real-time HTTP event callbacks, verify cryptographic HMAC-SHA256 signatures, test live payloads, and inspect delivery telemetry.
Quick Reference#
| Endpoint | Method | Required Scope | Description |
|---|---|---|---|
/v1/webhooks/endpoints | GET | webhooks:read | List all registered webhook endpoints for the account. |
/v1/webhooks/endpoints | POST | webhooks:write | Register a new HTTPS webhook destination and generate a signing secret. |
/v1/webhooks/endpoints/:id | GET | webhooks:read | Retrieve configuration and metadata for a specific webhook endpoint. |
/v1/webhooks/endpoints/:id | PATCH | webhooks:write | Update webhook endpoint name, target URL, payload format, or active state. |
/v1/webhooks/endpoints/:id/rotate-secret | POST | webhooks:write | Regenerate and immediately activate a fresh cryptographic HMAC signing secret. |
/v1/webhooks/endpoints/:id | DELETE | webhooks:write | Permanently remove a webhook endpoint. |
/v1/webhooks/test | POST | webhooks:write | Dispatch an immediate simulated event payload with anti-SSRF validation. |
/v1/webhooks/deliveries | GET | webhooks:read | Query paginated execution logs, response status codes, and latencies. |
/v1/webhooks/deliveries/export | GET | webhooks:read | Export complete delivery audit history in CSV or JSON format (Business tier). |
Plan Quotas & Tier Limits#
| Tier | Maximum Endpoints | Supported Formats | Retention & Export | Behavior When Limit Reached |
|---|---|---|---|---|
| Free | 0 endpoints | None | None | Returns HTTP 403 LIMIT_REACHED ("Webhook endpoint limit reached for your subscription plan. Please upgrade to Pro or Business."). |
| Pro | 2 endpoints | parsed_json, metadata_only, slack, discord | 7-day retention | Returns HTTP 403 LIMIT_REACHED upon creating 3rd endpoint. raw_mime returns 403 FEATURE_LOCKED. |
| Business | Unlimited (-1) | All 5 formats including raw_mime | 30-day retention + CSV/JSON Export | Unrestricted webhook endpoints and streaming MIME streams. |
Webhook Architecture & Event Flow#
When incoming messages match routing rules or trigger automated workflows, AliasFleet streams structured event payloads directly to your HTTPS servers:
sequenceDiagram autonumber actor Sender as Inbound Email Sender participant Engine as AliasFleet Ingestion Engine participant Rules as Rules Engine participant Egress as Anti-SSRF Dispatcher participant Customer as Your Webhook Server (HTTPS) Sender->>Engine: Inbound Message Envelope Engine->>Rules: Evaluate Inbound Rules Rules->>Rules: Action: send_to_webhook (Endpoint ID) Rules->>Egress: Assemble Payload & Sign HMAC-SHA256 Egress->>Egress: Validate Egress IP (Reject RFC 1918 / Metadata) Egress->>Customer: POST /webhook (X-AliasFleet-Signature) Customer-->>Egress: 200 OK Egress->>Engine: Record Delivery Log (Latency & Status)Cryptographic Signature Verification#
AliasFleet cryptographically signs all webhook events using HMAC-SHA256. Validating signatures ensures payloads originate from AliasFleet and prevents tampering, man-in-the-middle attacks, and replay vulnerabilities.
Slack & Discord Webhooks: Slack and Discord incoming webhooks authenticate using the secret token embedded directly in your webhook URL. No HMAC signature header is sent or required for these platforms.
Webhook Request Headers#
Every custom HTTP webhook request sent by AliasFleet includes three verification headers:
| Header | Description | Example |
|---|---|---|
X-AliasFleet-Signature | Timestamp and HMAC-SHA256 signature hash | t=1787652000,v1=9f83a21b4c7d0e9a8b7c6d5e4f3a... |
X-AliasFleet-Event | Event type identifier | email.received |
X-AliasFleet-Delivery | Unique delivery identifier for idempotency | whd_9z8y7x6w5v4u3t2s |
Signature Structure & Verification Algorithm#
The X-AliasFleet-Signature header contains two comma-separated key-value pairs:
t: Unix timestamp (in seconds) when the signature was computed.v1: The hexadecimal HMAC-SHA256 signature.
Signed Payload = timestamp + "." + rawRequestBodyExpected Signature = HMAC-SHA256(secret_key, Signed Payload)3-Step Verification Checklist#
- Parse Header: Split the header on
,and=to extracttandv1. - Prevent Replay Attacks: Reject requests where the difference between current Unix time and the header timestamp exceeds 300 seconds (5-minute tolerance window).
- Constant-Time Comparison: Recompute the signature from the raw, unparsed request body and compare using constant-time equality (
crypto.timingSafeEqualorhmac.compare_digest).
Use Raw Request Body: You must compute the HMAC over the exact, unparsed raw byte string received from the HTTP socket. Parsing JSON and re-stringifying it will alter whitespace and key ordering, causing verification to fail.
Production Verification Examples#
Next.js (App Router Route Handler)
// app/api/webhook/route.tsimport { NextResponse } from 'next/server';import crypto from 'node:crypto';export async function POST(req: Request) { const signatureHeader = req.headers.get('x-aliasfleet-signature'); const secretKey = process.env.ALIASFLEET_WEBHOOK_SECRET!; if (!signatureHeader || !secretKey) { return NextResponse.json({ error: 'Missing signature credentials' }, { status: 401 }); } // Read raw text before JSON parsing const rawBody = await req.text(); const parts = Object.fromEntries( signatureHeader.split(',').map((kv) => kv.trim().split('=')) ); const timestamp = parseInt(parts.t, 10); const receivedSig = parts.v1; if (!timestamp || !receivedSig) { return NextResponse.json({ error: 'Malformed signature header' }, { status: 400 }); } // Tolerance check (5 minutes) const now = Math.floor(Date.now() / 1000); if (Math.abs(now - timestamp) > 300) { return NextResponse.json({ error: 'Webhook timestamp expired' }, { status: 401 }); } // Verify HMAC-SHA256 const expectedSig = crypto .createHmac('sha256', secretKey) .update(`${timestamp}.${rawBody}`) .digest('hex'); const isValid = crypto.timingSafeEqual( Buffer.from(receivedSig, 'utf8'), Buffer.from(expectedSig, 'utf8') ); if (!isValid) { return NextResponse.json({ error: 'Signature verification failed' }, { status: 401 }); } const payload = JSON.parse(rawBody); console.log('Valid webhook payload:', payload.event, payload.message_id); return NextResponse.json({ success: true });}TypeScript / Node.js (Express)
import express from 'express';import crypto from 'node:crypto';const app = express();// Capture raw body buffer before JSON parsingapp.use( express.json({ verify: (req: any, _res, buf) => { req.rawBody = buf.toString('utf8'); }, }));function verifyWebhookSignature( rawBody: string, signatureHeader: string, secretKey: string, toleranceSeconds = 300): boolean { try { const parts = Object.fromEntries( signatureHeader.split(',').map((kv) => kv.trim().split('=')) ); const timestamp = parseInt(parts.t, 10); const receivedSig = parts.v1; if (!timestamp || !receivedSig) return false; // 1. Replay attack prevention const now = Math.floor(Date.now() / 1000); if (Math.abs(now - timestamp) > toleranceSeconds) { return false; } // 2. Compute expected HMAC const expectedSig = crypto .createHmac('sha256', secretKey) .update(`${timestamp}.${rawBody}`) .digest('hex'); // 3. Constant-time comparison return crypto.timingSafeEqual( Buffer.from(receivedSig, 'utf8'), Buffer.from(expectedSig, 'utf8') ); } catch { return false; }}app.post('/api/webhook', (req: any, res) => { const signatureHeader = req.headers['x-aliasfleet-signature'] as string; const secretKey = process.env.ALIASFLEET_WEBHOOK_SECRET!; if (!signatureHeader || !verifyWebhookSignature(req.rawBody, signatureHeader, secretKey)) { return res.status(401).json({ error: 'Invalid webhook signature' }); } const event = req.body; console.log(`Received email event ${event.event} for alias ${event.alias}`); return res.status(200).json({ received: true });});Python 3 (FastAPI)
import hmacimport hashlibimport timefrom fastapi import FastAPI, Request, HTTPException, Headerapp = FastAPI()WEBHOOK_SECRET = "af_whsec_your_signing_secret_here"@app.post("/api/webhook")async def handle_webhook( request: Request, x_aliasfleet_signature: str = Header(None)): if not x_aliasfleet_signature: raise HTTPException(status_code=401, detail="Missing signature header") # Read raw body bytes raw_body = await request.body() raw_text = raw_body.decode("utf-8") # Parse t and v1 parts = dict(item.split("=") for item in x_aliasfleet_signature.split(",")) timestamp = int(parts.get("t", 0)) received_sig = parts.get("v1", "") # Replay protection (5-minute window) if abs(int(time.time()) - timestamp) > 300: raise HTTPException(status_code=401, detail="Timestamp out of tolerance window") # Compute expected signature signed_payload = f"{timestamp}.{raw_text}".encode("utf-8") expected_sig = hmac.new( WEBHOOK_SECRET.encode("utf-8"), signed_payload, hashlib.sha256 ).hexdigest() # Timing-safe comparison if not hmac.compare_digest(received_sig, expected_sig): raise HTTPException(status_code=401, detail="Invalid signature") payload = await request.json() return {"status": "ok", "event": payload.get("event")}Go (net/http)
package mainimport ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io" "math" "net/http" "strconv" "strings" "time")func VerifyWebhook(rawBody []byte, signatureHeader string, secret string) bool { var timestamp int64 var receivedSig string for _, part := range strings.Split(signatureHeader, ",") { kv := strings.SplitN(strings.TrimSpace(part), "=", 2) if len(kv) == 2 { if kv[0] == "t" { timestamp, _ = strconv.ParseInt(kv[1], 10, 64) } else if kv[0] == "v1" { receivedSig = kv[1] } } } if timestamp == 0 || receivedSig == "" { return false } // 5-minute replay window now := time.Now().Unix() if math.Abs(float64(now-timestamp)) > 300 { return false } payloadToSign := fmt.Sprintf("%d.%s", timestamp, string(rawBody)) mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(payloadToSign)) expectedSig := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(receivedSig), []byte(expectedSig))}List Webhook Endpoints#
GET /v1/webhooks/endpoints
Retrieves all registered webhook endpoints configured for the authenticated account.
webhooks:readRate Limit: 60 req/minIdempotent: YescURL Example#
curl -X GET "https://api.aliasfleet.com/v1/webhooks/endpoints" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Accept: application/json"Response (200 OK)#
{ "endpoints": [ { "id": "wh_e0a1b2c3d4e5f6a7", "name": "Production Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "payload_format": "parsed_json", "is_active": true, "failure_count": 0, "last_triggered_at": "2026-09-14T12:00:00.000Z", "created_at": "2026-09-01T08:30:00.000Z", "updated_at": "2026-09-14T12:00:00.000Z" } ]}Get Webhook Endpoint by ID#
GET /v1/webhooks/endpoints/:id
Retrieves detailed configuration for a specific webhook endpoint.
webhooks:readRate Limit: 60 req/minIdempotent: YesPath Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook endpoint identifier (wh_... or legacy UUID). |
cURL Example#
curl -X GET "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Accept: application/json"Response (200 OK)#
{ "endpoint": { "id": "wh_e0a1b2c3d4e5f6a7", "user_id": "d3b07384-d113-4674-bc50-a88981298418", "name": "Production Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "secret_key": "af_whsec_9f83a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b", "payload_format": "parsed_json", "is_active": true, "failure_count": 0, "last_triggered_at": "2026-09-14T12:00:00.000Z", "created_at": "2026-09-01T08:30:00.000Z", "updated_at": "2026-09-14T12:00:00.000Z" }}Create Webhook Endpoint#
POST /v1/webhooks/endpoints
Registers a new HTTPS webhook destination, generates a cryptographically secure HMAC signing secret (af_whsec_...), and validates destination constraints.
webhooks:writeRate Limit: 30 req/minIdempotent: NoRequest Body Schema#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
name | string | Yes | 1–100 chars | Descriptive label for this webhook destination. |
url | string | Yes | Max 1000 chars | Public HTTPS/HTTP URL to receive POST events. |
payload_format | string | Optional | "parsed_json" | "metadata_only" | "raw_mime" | "slack" | "discord" (Default: "parsed_json") | Data format transmitted during deliveries. Note: raw_mime requires a Business plan. Slack and Discord require matching incoming webhook URLs. |
is_active | boolean | Optional | Default: true | Whether this endpoint actively accepts event deliveries. |
cURL Example#
curl -X POST "https://api.aliasfleet.com/v1/webhooks/endpoints" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "payload_format": "parsed_json", "is_active": true }'Response (201 Created)#
{ "message": "Webhook endpoint created successfully", "endpoint": { "id": "wh_e0a1b2c3d4e5f6a7", "user_id": "d3b07384-d113-4674-bc50-a88981298418", "name": "Production Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "secret_key": "af_whsec_9f83a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b", "payload_format": "parsed_json", "is_active": true, "failure_count": 0, "last_triggered_at": null, "created_at": "2026-09-14T15:00:00.000Z", "updated_at": "2026-09-14T15:00:00.000Z" }}Secret Key Management: Webhook signing secrets are symmetric and stored securely in the database. You can retrieve, reveal, or rotate them at any time via the dashboard or through the API.
Error Responses#
| Status Code | Code | Error Message | Reason |
|---|---|---|---|
400 | INVALID_URL | "URL must use http or https protocol" | Target URL must be a valid HTTP or HTTPS address. |
400 | INVALID_SLACK_URL | "Slack endpoints must be a valid Slack webhook URL (https://hooks.slack.com/services/T.../B.../...)" | Malformed Slack webhook URL for slack format. |
400 | INVALID_DISCORD_URL | "Discord endpoints must be a valid Discord webhook URL (https://discord.com/api/webhooks/{id}/{token})" | Malformed Discord webhook URL for discord format. |
403 | LIMIT_REACHED | "Webhook endpoint limit reached for your subscription plan. Please upgrade to Pro or Business." | Quota reached for current subscription tier. |
403 | FEATURE_LOCKED | "Raw MIME webhook streaming is only available on the Business plan." | Account tier does not support raw_mime format. |
Update Webhook Endpoint#
PATCH /v1/webhooks/endpoints/:id
Modifies an existing webhook endpoint configuration.
webhooks:writeRate Limit: 30 req/minIdempotent: YesPath Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook endpoint identifier (wh_... or legacy UUID). |
Request Body Schema#
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Optional | Updated descriptive label (1–100 chars). |
url | string | Optional | Updated target HTTPS/HTTP destination URL. |
payload_format | string | Optional | "parsed_json" | "metadata_only" | "raw_mime" | "slack" | "discord". |
is_active | boolean | Optional | Enable or disable event dispatches. |
cURL Example#
curl -X PATCH "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Ingestion Pipeline", "is_active": true }'Response (200 OK)#
{ "message": "Webhook endpoint updated successfully", "endpoint": { "id": "wh_e0a1b2c3d4e5f6a7", "name": "Updated Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "payload_format": "parsed_json", "is_active": true, "failure_count": 0, "last_triggered_at": "2026-09-14T12:00:00.000Z", "created_at": "2026-09-01T08:30:00.000Z", "updated_at": "2026-09-14T15:30:00.000Z" }}Error Responses#
| Status Code | Code | Error Message | Reason |
|---|---|---|---|
400 | INVALID_URL | "URL must use http or https protocol" | Target URL must be a valid HTTP or HTTPS address. |
400 | INVALID_SLACK_URL | "Slack endpoints must be a valid Slack webhook URL (https://hooks.slack.com/services/T.../B.../...)" | Malformed Slack webhook URL for slack format. |
400 | INVALID_DISCORD_URL | "Discord endpoints must be a valid Discord webhook URL (https://discord.com/api/webhooks/{id}/{token})" | Malformed Discord webhook URL for discord format. |
403 | FEATURE_LOCKED | "Raw MIME webhook streaming is only available on the Business plan." | Account tier does not support raw_mime format. |
404 | NOT_FOUND | "Webhook endpoint not found" | Endpoint does not exist or belongs to another user. |
Rotate Webhook Secret#
POST /v1/webhooks/endpoints/:id/rotate-secret
Regenerates and immediately activates a fresh cryptographic HMAC-SHA256 signing secret (af_whsec_...) for a target webhook endpoint.
webhooks:writeRate Limit: 30 req/minIdempotent: NoPath Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook endpoint identifier (wh_... or legacy UUID). |
Request Headers#
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer afp_... |
Content-Type | string | Optional | application/json |
cURL Example#
curl -X POST "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7/rotate-secret" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Content-Type: application/json"Response (200 OK)#
{ "message": "Webhook secret rotated successfully", "secret_key": "af_whsec_8f93a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b", "endpoint": { "id": "wh_e0a1b2c3d4e5f6a7", "user_id": "d3b07384-d113-4674-bc50-a88981298418", "name": "Production Ingestion Pipeline", "url": "https://api.mycompany.com/v1/emails/webhook", "payload_format": "parsed_json", "is_active": true, "secret_key": "af_whsec_8f93a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b", "failure_count": 0, "last_triggered_at": "2026-09-14T12:00:00.000Z", "created_at": "2026-08-25T12:00:00.000Z", "updated_at": "2026-09-14T15:35:00.000Z" }}Immediate Invalidation: Save the new secret_key in your server environment configuration immediately. Inbound webhooks sent after this request will be signed with the new secret, and signatures generated with the revoked secret will fail verification.
Error Responses#
| Status Code | Code | Error Message | Reason |
|---|---|---|---|
401 | UNAUTHORIZED | "Unauthorized: Missing API Key or Session Token" | Missing or invalid Bearer token. |
403 | INSUFFICIENT_SCOPES | "Forbidden: Insufficient scopes" | Access token lacks webhooks:write. |
404 | NOT_FOUND | "Webhook endpoint not found" | Endpoint does not exist or belongs to another user. |
500 | DATABASE_ERROR | "Database operation failed" | Internal database error during secret rotation. |
Delete Webhook Endpoint#
DELETE /v1/webhooks/endpoints/:id
Permanently removes a webhook endpoint. Inbound rules routing to this endpoint will cease webhook dispatches.
webhooks:writeRate Limit: 30 req/minIdempotent: YesPath Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook endpoint identifier (wh_... or legacy UUID). |
cURL Example#
curl -X DELETE "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e"Response (200 OK)#
{ "success": true, "message": "Webhook endpoint deleted successfully"}Test Webhook Dispatch#
POST /v1/webhooks/test
Dispatches an immediate mock event payload (email.received) to verify network connectivity, SSL certificate handshakes, and HMAC signature computation.
Anti-SSRF Protected & Logged: The test runner enforces identical security boundaries as production deliveries, rejecting private subnets, cloud metadata endpoints, and non-routable hostnames. Each test dispatch records an audit delivery attempt in webhook_deliveries (with rule_id: null) and updates endpoint health metrics (last_triggered_at, failure_count), even on socket errors or timeouts (status_code: 0).
webhooks:writeRate Limit: 30 req/minIdempotent: NoRequest Body Schema#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
endpoint_id | string | Optional | Valid endpoint ID | Existing webhook endpoint ID (wh_...) to test directly. |
url | string | Yes | Valid URL | Target HTTPS endpoint URL to receive test event. |
secret_key | string | Optional | 16–128 chars | Secret used to sign the test dispatch. If omitted, existing endpoint secret or temporary secret is used. |
payload_format | string | Optional | "parsed_json" | "metadata_only" | "raw_mime" | "slack" | "discord" | Format of test event payload (Default: "parsed_json"). |
cURL Example#
curl -X POST "https://api.aliasfleet.com/v1/webhooks/test" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Content-Type: application/json" \ -d '{ "endpoint_id": "wh_e0a1b2c3d4e5f6a7", "url": "https://api.mycompany.com/v1/emails/webhook", "payload_format": "parsed_json" }'Response (200 OK)#
{ "success": true, "status_code": 200, "response_time_ms": 142, "response_body": "{\"received\": true}", "signature_header": "t=1756972800,v1=9f83a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b", "dispatched_payload": "{\"event\":\"email.received\",\"timestamp\":\"2026-09-14T15:00:00.000Z\",\"message_id\":\"<test_e0a1b2c3@aliasfleet.com>\",\"alias\":\"test-alias@user.aliasfleet.com\",\"sender\":{\"address\":\"billing@example.com\",\"name\":\"Example Billing\"},\"recipient\":\"test-alias@user.aliasfleet.com\",\"subject\":\"[Test] AliasFleet Webhook Dispatch Simulator\"}", "signing_secret": "af_whsec_9f83a21b4c7d0e9a8b7c6d5e4f3a2b1c8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b"}Query Webhook Deliveries#
GET /v1/webhooks/deliveries
Retrieves audit logs of past webhook dispatches, including HTTP status codes, execution response latencies, error messages, and payload snippets.
Retention Windows: Deliveries are retained for 7 days on the Pro tier and 30 days on the Business tier. Responses include active retention parameters and stable unfiltered totals.
webhooks:readRate Limit: 60 req/minIdempotent: YesQuery Parameters#
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
endpoint_id | string | Optional | None | Filter delivery records by a specific webhook endpoint ID (wh_...). |
status | string | Optional | "all" | Filter by status: "all", "failed" (non-2xx or connection errors), or "success" (2xx). |
limit | integer | Optional | 20 | Number of logs to retrieve (1–50). |
offset | integer | Optional | 0 | Number of records to skip for pagination. |
cURL Example#
curl -X GET "https://api.aliasfleet.com/v1/webhooks/deliveries?limit=10&status=failed" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -H "Accept: application/json"Response (200 OK)#
{ "deliveries": [ { "id": "whd_d0a1b2c3d4e5f6a7", "webhook_endpoint_id": "wh_e0a1b2c3d4e5f6a7", "rule_id": "rule_a1b2c3d4e5f67a8b", "status_code": 200, "response_time_ms": 94, "error_message": null, "payload_snippet": { "event": "email.received", "recipient": "orders.749@afinbox.com", "subject": "Monthly Subscription Invoice" }, "created_at": "2026-09-14T15:32:10.000Z" } ], "pagination": { "total": 48, "total_unfiltered": 120, "limit": 10, "offset": 0 }, "retention": { "days": 30, "plan": "business", "is_business": true }}Response Fields#
| Field | Type | Description |
|---|---|---|
deliveries | array | List of delivery execution records. |
deliveries[].id | string | Unique identifier for the delivery execution log (whd_...). |
deliveries[].webhook_endpoint_id | string | ID of the target webhook endpoint (wh_...). |
deliveries[].rule_id | string | null | ID of the routing rule that triggered this dispatch (rule_...), or null for manual tests. |
deliveries[].status_code | integer | HTTP status code returned by the destination receiver (e.g. 200, 500, or 0 on socket/timeout failure). |
deliveries[].response_time_ms | integer | Round-trip latency in milliseconds. |
deliveries[].error_message | string | null | Error description if the delivery failed or timed out. |
deliveries[].payload_snippet | object | null | Metadata snippet containing event type, recipient, and subject. |
deliveries[].created_at | string | ISO 8601 timestamp when the delivery was initiated. |
pagination.total | integer | Total delivery log count matching query criteria. |
pagination.total_unfiltered | integer | Stable total delivery log count across the active retention window. |
pagination.limit | integer | Page limit used for query. |
pagination.offset | integer | Offset applied. |
retention.days | integer | Number of days delivery logs are retained (7 for Pro, 30 for Business). |
retention.plan | string | Active subscription plan key. |
retention.is_business | boolean | Whether account possesses Business tier retention entitlements. |
Export Webhook Deliveries#
GET /v1/webhooks/deliveries/export
Exports delivery audit logs in streaming CSV or JSON format. This endpoint is exclusive to the Business plan and retrieves historical delivery records up to the 30-day retention window.
webhooks:readRate Limit: 10 req/minPlan: Business OnlyIdempotent: YesQuery Parameters#
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
format | string | Optional | "csv" | Export serialization format: "csv" or "json". |
endpoint_id | string | Optional | None | Filter exported records by a specific webhook endpoint ID (wh_...). |
status | string | Optional | "all" | Filter by status: "all" or "failed". |
Export Columns (CSV Format)#
The streaming CSV export formats 15 RFC-4180 escaped columns:
Delivery IDCreated At (UTC)Endpoint IDStatus CodeResponse Time (ms)Error MessageEventFormatSender (From)Recipient (To)SubjectSize (Bytes)Attachment CountPreviewFull Payload (JSON)
cURL Example#
curl -X GET "https://api.aliasfleet.com/v1/webhooks/deliveries/export?format=csv&status=failed" \ -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \ -o "webhook_failed_deliveries.csv"Error Responses#
| Status Code | Code | Error Message | Reason |
|---|---|---|---|
401 | UNAUTHORIZED | "Unauthorized" | Missing or invalid API key. |
403 | FEATURE_GATED | "Delivery audit log export is exclusive to the Business tier." | Account is on Free or Pro tier. |
403 | INSUFFICIENT_SCOPES | "Forbidden: Insufficient scopes" | Access token lacks webhooks:read. |
Standard Event Types#
The table below catalogs events dispatched across webhook integrations:
| Event Identifier | Trigger Condition | Common Use Cases |
|---|---|---|
email.received | Inbound message accepted by AliasFleet and evaluated by routing rules. | Parsing transactional receipts, feeding AI assistants, pipeline ingestion. |
email.forwarded | Message successfully delivered to destination inbox. | Delivery audit tracking, latency monitoring. |
email.bounced | Inbound or forwarded message encountered an upstream SMTP delivery failure. | Automated recipient deactivation, bounce alerting. |
email.blocked | Message dropped or rejected due to firewall rules, denylists, or spam scoring. | Security telemetry, threat logging. |