HomeDocsBlogSupport
OverviewWhat is AliasFleet?Creating an AliasEmails Not ArrivingContact Support

Getting Started

Getting Started
  • What is AliasFleet?
  • Your First Alias
  • Understanding Forwarding
  • Your Dashboard at a Glance
  • Using the Browser Extension

Account & Billing

Account & Billing
  • Your Plan and Usage Limits
  • Upgrading to Pro
  • Managing Your Subscription
  • Downloading Invoices
  • Support Ticket Limits
  • Membership Tier
  • Billing Cycle and Renewal
  • API Rate Limits by Plan

Billing Support

Account & Billing
  • Cancelling Your Subscription
  • Downgrading Your AliasFleet Plan
  • Understanding Proration and Plan Changes
  • Updating Your Payment Method
  • Handling Failed Payments and Account Suspension
  • AliasFleet Refund Policy

Email Aliases

Email Aliases
  • How to Set Up an Email Alias
  • Activating and Deactivating an Alias
  • How to Delete an Email Alias
  • Sorting, Filtering and Searching Aliases
  • Grid View vs List View
  • Grouping Aliases
  • Alias Categories
  • Copying an Alias Address
  • Per-Alias Email Banner Settings
  • Alias Permission Mode: Strict vs Open
  • Sending Outbound Emails (Quick Send)

Custom Domains

Custom Domains
  • Adding a Custom Domain
  • DNS Verification
  • Domain Status and Health Indicators
  • Subdomains
  • Removing a Domain
  • Using Your Domain on Aliases

Destinations

Destinations
  • What Is a Destination?
  • Adding a Destination
  • Verifying a Destination
  • Setting a Default Destination
  • Reply from Aliases
  • Send New Emails from Aliases
  • Understanding Email Threading & Replies
  • One-Click Enable from Bounce Email
  • Removing a Destination
  • PGP Encryption for Destinations
  • WKD Auto-Encryption for Replies & Sends

Security & Privacy

Security & Privacy
  • Security Best Practices
  • Two-Factor Authentication (2FA)
  • Active Sessions
  • Changing Your Password
  • What Data AliasFleet Stores
  • Reporting a Security Issue
  • Browser Extension Sessions
  • How to Use Vault Lock in the Extension

Rate Limiting

Security & Privacy
  • Rate Limiting and Account Protection

Settings

Settings
  • General Settings
  • Profile Settings
  • Alias Settings
  • Destinations in Settings
  • Notification Settings
  • Security Settings
  • Deleting Your Account

Analytics

Analytics
  • Analytics Overview
  • Alias Performance
  • Top Senders
  • Trends
  • Bounces
  • Bandwidth Usage
  • Category Breakdown
  • Exporting Analytics Data
  • Understanding Alias Statistics
  • How Monthly Trends Work

Sender Rules

Sender Rules
  • Using Sender Rules
  • Blacklist vs Deactivating an Alias
  • Using Allowlist Rules to Allow Senders
  • Blocked Emails Explained

Troubleshooting

Troubleshooting
  • Emails Not Arriving
  • Can't Verify a Destination
  • DNS Not Verifying
  • Can't Log In
  • Replies Not Going Through Alias
  • Alias Not Forwarding
  • Payment Failed
  • Browser Extension Issues
  • Too Many Requests Error
  • Page Not Loading or Showing an Error

Developers

Developer API
  • Identity & Token Introspection API
  • Aliases API
  • Alias Destinations & Batch Operations API
  • Domains API
  • Destinations API
  • Quick-Send API
  • Sender Rules API
  • Activity & Audit Logs API
  • Fleet Analytics API
  • Rules API
  • Security & Threat Intelligence API
  • Webhooks API
  • Categories & Tags API
  • User Profile & Account API
PrivacyTermsCookies
Article Navigation
OverviewWhat is AliasFleet?Creating an AliasEmails Not ArrivingContact Support

Getting Started

Getting Started
  • What is AliasFleet?
  • Your First Alias
  • Understanding Forwarding
  • Your Dashboard at a Glance
  • Using the Browser Extension

Account & Billing

Account & Billing
  • Your Plan and Usage Limits
  • Upgrading to Pro
  • Managing Your Subscription
  • Downloading Invoices
  • Support Ticket Limits
  • Membership Tier
  • Billing Cycle and Renewal
  • API Rate Limits by Plan

Billing Support

Account & Billing
  • Cancelling Your Subscription
  • Downgrading Your AliasFleet Plan
  • Understanding Proration and Plan Changes
  • Updating Your Payment Method
  • Handling Failed Payments and Account Suspension
  • AliasFleet Refund Policy

Email Aliases

Email Aliases
  • How to Set Up an Email Alias
  • Activating and Deactivating an Alias
  • How to Delete an Email Alias
  • Sorting, Filtering and Searching Aliases
  • Grid View vs List View
  • Grouping Aliases
  • Alias Categories
  • Copying an Alias Address
  • Per-Alias Email Banner Settings
  • Alias Permission Mode: Strict vs Open
  • Sending Outbound Emails (Quick Send)

Custom Domains

Custom Domains
  • Adding a Custom Domain
  • DNS Verification
  • Domain Status and Health Indicators
  • Subdomains
  • Removing a Domain
  • Using Your Domain on Aliases

Destinations

Destinations
  • What Is a Destination?
  • Adding a Destination
  • Verifying a Destination
  • Setting a Default Destination
  • Reply from Aliases
  • Send New Emails from Aliases
  • Understanding Email Threading & Replies
  • One-Click Enable from Bounce Email
  • Removing a Destination
  • PGP Encryption for Destinations
  • WKD Auto-Encryption for Replies & Sends

Security & Privacy

Security & Privacy
  • Security Best Practices
  • Two-Factor Authentication (2FA)
  • Active Sessions
  • Changing Your Password
  • What Data AliasFleet Stores
  • Reporting a Security Issue
  • Browser Extension Sessions
  • How to Use Vault Lock in the Extension

Rate Limiting

Security & Privacy
  • Rate Limiting and Account Protection

Settings

Settings
  • General Settings
  • Profile Settings
  • Alias Settings
  • Destinations in Settings
  • Notification Settings
  • Security Settings
  • Deleting Your Account

Analytics

Analytics
  • Analytics Overview
  • Alias Performance
  • Top Senders
  • Trends
  • Bounces
  • Bandwidth Usage
  • Category Breakdown
  • Exporting Analytics Data
  • Understanding Alias Statistics
  • How Monthly Trends Work

Sender Rules

Sender Rules
  • Using Sender Rules
  • Blacklist vs Deactivating an Alias
  • Using Allowlist Rules to Allow Senders
  • Blocked Emails Explained

Troubleshooting

Troubleshooting
  • Emails Not Arriving
  • Can't Verify a Destination
  • DNS Not Verifying
  • Can't Log In
  • Replies Not Going Through Alias
  • Alias Not Forwarding
  • Payment Failed
  • Browser Extension Issues
  • Too Many Requests Error
  • Page Not Loading or Showing an Error

Developers

Developer API
  • Identity & Token Introspection API
  • Aliases API
  • Alias Destinations & Batch Operations API
  • Domains API
  • Destinations API
  • Quick-Send API
  • Sender Rules API
  • Activity & Audit Logs API
  • Fleet Analytics API
  • Rules API
  • Security & Threat Intelligence API
  • Webhooks API
  • Categories & Tags API
  • User Profile & Account API
PrivacyTermsCookies
Developer API
Docs
Developer API
Webhooks API

Webhooks API

Configure real-time HTTP event callbacks, verify cryptographic HMAC-SHA256 signatures, test live payloads, and inspect delivery telemetry.

20 min read
Updated September 4, 2026
Quick ReferencePlan Quotas & Tier LimitsWebhook Architecture & Event FlowCryptographic Signature VerificationWebhook Request HeadersSignature Structure & Verification Algorithm3-Step Verification ChecklistProduction Verification ExamplesList Webhook EndpointscURL ExampleResponse (200 OK)Get Webhook Endpoint by IDPath ParameterscURL ExampleResponse (200 OK)Create Webhook EndpointRequest Body SchemacURL ExampleResponse (201 Created)Error ResponsesUpdate Webhook EndpointPath ParametersRequest Body SchemacURL ExampleResponse (200 OK)Error ResponsesRotate Webhook SecretPath ParametersRequest HeaderscURL ExampleResponse (200 OK)Error ResponsesDelete Webhook EndpointPath ParameterscURL ExampleResponse (200 OK)Test Webhook DispatchRequest Body SchemacURL ExampleResponse (200 OK)Query Webhook DeliveriesQuery ParameterscURL ExampleResponse (200 OK)Response FieldsExport Webhook DeliveriesQuery ParametersExport Columns (CSV Format)cURL ExampleError ResponsesStandard Event Types

Configure real-time HTTP event callbacks, verify cryptographic HMAC-SHA256 signatures, test live payloads, and inspect delivery telemetry.


Quick Reference#

EndpointMethodRequired ScopeDescription
/v1/webhooks/endpointsGETwebhooks:readList all registered webhook endpoints for the account.
/v1/webhooks/endpointsPOSTwebhooks:writeRegister a new HTTPS webhook destination and generate a signing secret.
/v1/webhooks/endpoints/:idGETwebhooks:readRetrieve configuration and metadata for a specific webhook endpoint.
/v1/webhooks/endpoints/:idPATCHwebhooks:writeUpdate webhook endpoint name, target URL, payload format, or active state.
/v1/webhooks/endpoints/:id/rotate-secretPOSTwebhooks:writeRegenerate and immediately activate a fresh cryptographic HMAC signing secret.
/v1/webhooks/endpoints/:idDELETEwebhooks:writePermanently remove a webhook endpoint.
/v1/webhooks/testPOSTwebhooks:writeDispatch an immediate simulated event payload with anti-SSRF validation.
/v1/webhooks/deliveriesGETwebhooks:readQuery paginated execution logs, response status codes, and latencies.
/v1/webhooks/deliveries/exportGETwebhooks:readExport complete delivery audit history in CSV or JSON format (Business tier).
Data comparison table

Plan Quotas & Tier Limits#

TierMaximum EndpointsSupported FormatsRetention & ExportBehavior When Limit Reached
Free0 endpointsNoneNoneReturns HTTP 403 LIMIT_REACHED ("Webhook endpoint limit reached for your subscription plan. Please upgrade to Pro or Business.").
Pro2 endpointsparsed_json, metadata_only, slack, discord7-day retentionReturns HTTP 403 LIMIT_REACHED upon creating 3rd endpoint. raw_mime returns 403 FEATURE_LOCKED.
BusinessUnlimited (-1)All 5 formats including raw_mime30-day retention + CSV/JSON ExportUnrestricted webhook endpoints and streaming MIME streams.
Data comparison table

Webhook Architecture & Event Flow#

When incoming messages match routing rules or trigger automated workflows, AliasFleet streams structured event payloads directly to your HTTPS servers:

MERMAID
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.

Note

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:

HeaderDescriptionExample
X-AliasFleet-SignatureTimestamp and HMAC-SHA256 signature hasht=1787652000,v1=9f83a21b4c7d0e9a8b7c6d5e4f3a...
X-AliasFleet-EventEvent type identifieremail.received
X-AliasFleet-DeliveryUnique delivery identifier for idempotencywhd_9z8y7x6w5v4u3t2s
Data comparison table

Signature Structure & Verification Algorithm#

The X-AliasFleet-Signature header contains two comma-separated key-value pairs:

  1. t: Unix timestamp (in seconds) when the signature was computed.
  2. v1: The hexadecimal HMAC-SHA256 signature.
Signed Payload     = timestamp + "." + rawRequestBodyExpected Signature = HMAC-SHA256(secret_key, Signed Payload)

3-Step Verification Checklist#

  1. Parse Header: Split the header on , and = to extract t and v1.
  2. Prevent Replay Attacks: Reject requests where the difference between current Unix time and the header timestamp exceeds 300 seconds (5-minute tolerance window).
  3. Constant-Time Comparison: Recompute the signature from the raw, unparsed request body and compare using constant-time equality (crypto.timingSafeEqual or hmac.compare_digest).
Important

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)

TypeScript
// 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)

TypeScript
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)

Python
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)

Go
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.

Scope: webhooks:readRate Limit: 60 req/minIdempotent: Yes

cURL Example#

Terminal
curl -X GET "https://api.aliasfleet.com/v1/webhooks/endpoints" \  -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \  -H "Accept: application/json"

Response (200 OK)#

JSON
{  "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.

Scope: webhooks:readRate Limit: 60 req/minIdempotent: Yes

Path Parameters#

ParameterTypeRequiredDescription
idstringYesWebhook endpoint identifier (wh_... or legacy UUID).
Data comparison table

cURL Example#

Terminal
curl -X GET "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7" \  -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e" \  -H "Accept: application/json"

Response (200 OK)#

JSON
{  "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.

Scope: webhooks:writeRate Limit: 30 req/minIdempotent: No

Request Body Schema#

FieldTypeRequiredConstraintsDescription
namestringYes1–100 charsDescriptive label for this webhook destination.
urlstringYesMax 1000 charsPublic HTTPS/HTTP URL to receive POST events.
payload_formatstringOptional"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_activebooleanOptionalDefault: trueWhether this endpoint actively accepts event deliveries.
Data comparison table

cURL Example#

Terminal
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)#

JSON
{  "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"  }}
Note

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 CodeCodeError MessageReason
400INVALID_URL"URL must use http or https protocol"Target URL must be a valid HTTP or HTTPS address.
400INVALID_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.
400INVALID_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.
403LIMIT_REACHED"Webhook endpoint limit reached for your subscription plan. Please upgrade to Pro or Business."Quota reached for current subscription tier.
403FEATURE_LOCKED"Raw MIME webhook streaming is only available on the Business plan."Account tier does not support raw_mime format.
Data comparison table

Update Webhook Endpoint#

PATCH /v1/webhooks/endpoints/:id

Modifies an existing webhook endpoint configuration.

Scope: webhooks:writeRate Limit: 30 req/minIdempotent: Yes

Path Parameters#

ParameterTypeRequiredDescription
idstringYesWebhook endpoint identifier (wh_... or legacy UUID).
Data comparison table

Request Body Schema#

FieldTypeRequiredDescription
namestringOptionalUpdated descriptive label (1–100 chars).
urlstringOptionalUpdated target HTTPS/HTTP destination URL.
payload_formatstringOptional"parsed_json" | "metadata_only" | "raw_mime" | "slack" | "discord".
is_activebooleanOptionalEnable or disable event dispatches.
Data comparison table

cURL Example#

Terminal
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)#

JSON
{  "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 CodeCodeError MessageReason
400INVALID_URL"URL must use http or https protocol"Target URL must be a valid HTTP or HTTPS address.
400INVALID_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.
400INVALID_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.
403FEATURE_LOCKED"Raw MIME webhook streaming is only available on the Business plan."Account tier does not support raw_mime format.
404NOT_FOUND"Webhook endpoint not found"Endpoint does not exist or belongs to another user.
Data comparison table

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.

Scope: webhooks:writeRate Limit: 30 req/minIdempotent: No

Path Parameters#

ParameterTypeRequiredDescription
idstringYesWebhook endpoint identifier (wh_... or legacy UUID).
Data comparison table

Request Headers#

HeaderTypeRequiredDescription
AuthorizationstringYesBearer afp_...
Content-TypestringOptionalapplication/json
Data comparison table

cURL Example#

Terminal
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)#

JSON
{  "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"  }}
Warning

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 CodeCodeError MessageReason
401UNAUTHORIZED"Unauthorized: Missing API Key or Session Token"Missing or invalid Bearer token.
403INSUFFICIENT_SCOPES"Forbidden: Insufficient scopes"Access token lacks webhooks:write.
404NOT_FOUND"Webhook endpoint not found"Endpoint does not exist or belongs to another user.
500DATABASE_ERROR"Database operation failed"Internal database error during secret rotation.
Data comparison table

Delete Webhook Endpoint#

DELETE /v1/webhooks/endpoints/:id

Permanently removes a webhook endpoint. Inbound rules routing to this endpoint will cease webhook dispatches.

Scope: webhooks:writeRate Limit: 30 req/minIdempotent: Yes

Path Parameters#

ParameterTypeRequiredDescription
idstringYesWebhook endpoint identifier (wh_... or legacy UUID).
Data comparison table

cURL Example#

Terminal
curl -X DELETE "https://api.aliasfleet.com/v1/webhooks/endpoints/wh_e0a1b2c3d4e5f6a7" \  -H "Authorization: Bearer afp_4a8f9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e"

Response (200 OK)#

JSON
{  "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.

Tip

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).

Scope: webhooks:writeRate Limit: 30 req/minIdempotent: No

Request Body Schema#

FieldTypeRequiredConstraintsDescription
endpoint_idstringOptionalValid endpoint IDExisting webhook endpoint ID (wh_...) to test directly.
urlstringYesValid URLTarget HTTPS endpoint URL to receive test event.
secret_keystringOptional16–128 charsSecret used to sign the test dispatch. If omitted, existing endpoint secret or temporary secret is used.
payload_formatstringOptional"parsed_json" | "metadata_only" | "raw_mime" | "slack" | "discord"Format of test event payload (Default: "parsed_json").
Data comparison table

cURL Example#

Terminal
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)#

JSON
{  "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.

Note

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.

Scope: webhooks:readRate Limit: 60 req/minIdempotent: Yes

Query Parameters#

ParameterTypeRequiredDefaultDescription
endpoint_idstringOptionalNoneFilter delivery records by a specific webhook endpoint ID (wh_...).
statusstringOptional"all"Filter by status: "all", "failed" (non-2xx or connection errors), or "success" (2xx).
limitintegerOptional20Number of logs to retrieve (1–50).
offsetintegerOptional0Number of records to skip for pagination.
Data comparison table

cURL Example#

Terminal
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)#

JSON
{  "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#

FieldTypeDescription
deliveriesarrayList of delivery execution records.
deliveries[].idstringUnique identifier for the delivery execution log (whd_...).
deliveries[].webhook_endpoint_idstringID of the target webhook endpoint (wh_...).
deliveries[].rule_idstring | nullID of the routing rule that triggered this dispatch (rule_...), or null for manual tests.
deliveries[].status_codeintegerHTTP status code returned by the destination receiver (e.g. 200, 500, or 0 on socket/timeout failure).
deliveries[].response_time_msintegerRound-trip latency in milliseconds.
deliveries[].error_messagestring | nullError description if the delivery failed or timed out.
deliveries[].payload_snippetobject | nullMetadata snippet containing event type, recipient, and subject.
deliveries[].created_atstringISO 8601 timestamp when the delivery was initiated.
pagination.totalintegerTotal delivery log count matching query criteria.
pagination.total_unfilteredintegerStable total delivery log count across the active retention window.
pagination.limitintegerPage limit used for query.
pagination.offsetintegerOffset applied.
retention.daysintegerNumber of days delivery logs are retained (7 for Pro, 30 for Business).
retention.planstringActive subscription plan key.
retention.is_businessbooleanWhether account possesses Business tier retention entitlements.
Data comparison table

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.

Scope: webhooks:readRate Limit: 10 req/minPlan: Business OnlyIdempotent: Yes

Query Parameters#

ParameterTypeRequiredDefaultDescription
formatstringOptional"csv"Export serialization format: "csv" or "json".
endpoint_idstringOptionalNoneFilter exported records by a specific webhook endpoint ID (wh_...).
statusstringOptional"all"Filter by status: "all" or "failed".
Data comparison table

Export Columns (CSV Format)#

The streaming CSV export formats 15 RFC-4180 escaped columns:

  1. Delivery ID
  2. Created At (UTC)
  3. Endpoint ID
  4. Status Code
  5. Response Time (ms)
  6. Error Message
  7. Event
  8. Format
  9. Sender (From)
  10. Recipient (To)
  11. Subject
  12. Size (Bytes)
  13. Attachment Count
  14. Preview
  15. Full Payload (JSON)

cURL Example#

Terminal
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 CodeCodeError MessageReason
401UNAUTHORIZED"Unauthorized"Missing or invalid API key.
403FEATURE_GATED"Delivery audit log export is exclusive to the Business tier."Account is on Free or Pro tier.
403INSUFFICIENT_SCOPES"Forbidden: Insufficient scopes"Access token lacks webhooks:read.
Data comparison table

Standard Event Types#

The table below catalogs events dispatched across webhook integrations:

Event IdentifierTrigger ConditionCommon Use Cases
email.receivedInbound message accepted by AliasFleet and evaluated by routing rules.Parsing transactional receipts, feeding AI assistants, pipeline ingestion.
email.forwardedMessage successfully delivered to destination inbox.Delivery audit tracking, latency monitoring.
email.bouncedInbound or forwarded message encountered an upstream SMTP delivery failure.Automated recipient deactivation, bounce alerting.
email.blockedMessage dropped or rejected due to firewall rules, denylists, or spam scoring.Security telemetry, threat logging.
Data comparison table

Frequently Asked Questions

AliasFleet transmits an X-AliasFleet-Signature header containing a timestamp and HMAC-SHA256 signature (t=timestamp,v1=signature). Compute the HMAC-SHA256 hex digest of `${timestamp}.${raw_payload}` using your secret key (af_whsec_...) and compare it using a constant-time comparison function.

AliasFleet supports five native serialization formats: parsed_json (default structured JSON with headers, senders, and text/HTML bodies), metadata_only (headers and envelope metrics without message body), raw_mime (complete RFC 5322 MIME stream, available on Business plans), slack (direct posting to Slack incoming webhooks), and discord (formatted Discord embed dispatches).

Every outbound webhook delivery is verified through an egress firewall filter that strictly prohibits calls to private subnets (RFC 1918), loopback interfaces (127.0.0.1, ::1), and cloud metadata endpoints (169.254.169.254).

Deliveries that return non-2xx status codes or exceed the 10-second timeout are logged in the delivery audit history with response latencies and error snippets. Endpoints also track consecutive failure counts.

Call POST /v1/webhooks/endpoints/:id/rotate-secret to provision and activate a fresh 32-byte signing secret (af_whsec_...). The new secret is active immediately with zero downtime for incoming events.

Was this article helpful?

PreviousSecurity & Threat Intelligence APINextCategories & Tags API

Related in Developer API

Identity & Token Introspection API

Verify token validity, inspect active permission scopes, and retrieve account metadata using the Identity API.

Aliases API

Create, list, inspect, update, and manage the full lifecycle of email aliases programmatically using the AliasFleet REST API.

Alias Destinations & Batch Operations API

Configure multi-destination forwarding fanout, execute atomic batch updates across up to 100 aliases, and fine-tune per-alias privacy settings.

Domains API

Query shared platform domains, register custom brand domains, provision subdomains, verify DNS records, and inspect catch-all forwarding rules.

Destinations API

Register destination inboxes, execute 6-digit OTP verification challenges, configure per-channel sender identities, and manage routing fallbacks.

On this page

Quick ReferencePlan Quotas & Tier LimitsWebhook Architecture & Event FlowCryptographic Signature VerificationWebhook Request HeadersSignature Structure & Verification Algorithm3-Step Verification ChecklistProduction Verification ExamplesList Webhook EndpointscURL ExampleResponse (200 OK)Get Webhook Endpoint by IDPath ParameterscURL ExampleResponse (200 OK)Create Webhook EndpointRequest Body SchemacURL ExampleResponse (201 Created)Error ResponsesUpdate Webhook EndpointPath ParametersRequest Body SchemacURL ExampleResponse (200 OK)Error ResponsesRotate Webhook SecretPath ParametersRequest HeaderscURL ExampleResponse (200 OK)Error ResponsesDelete Webhook EndpointPath ParameterscURL ExampleResponse (200 OK)Test Webhook DispatchRequest Body SchemacURL ExampleResponse (200 OK)Query Webhook DeliveriesQuery ParameterscURL ExampleResponse (200 OK)Response FieldsExport Webhook DeliveriesQuery ParametersExport Columns (CSV Format)cURL ExampleError ResponsesStandard Event Types

Still need help?

Cannot find the answer you are looking for? Our support team is ready to assist.

Contact Support