Setup Guide

This guide walks you through setting up webhook subscriptions, handling incoming events, verifying signatures, and managing your event delivery.

Creating a Subscription

Register your endpoint to start receiving events:

$curl -X POST 'https://api.airweave.ai/webhooks/subscriptions' \
> -H 'x-api-key: YOUR_API_KEY' \
> -H 'Content-Type: application/json' \
> -d '{
> "url": "https://your-server.com/webhooks/airweave",
> "event_types": ["sync.completed", "sync.failed"]
> }'

Response:

1{
2 "id": "ep_2bVxUn3RFnLYHa8z6ZKHMT9PqPX",
3 "url": "https://your-server.com/webhooks/airweave",
4 "filter_types": ["sync.completed", "sync.failed"],
5 "disabled": false,
6 "created_at": "2025-01-15T10:30:00Z",
7 "updated_at": "2025-01-15T10:30:00Z"
8}

You can optionally provide a custom secret (minimum 24 characters) for signature verification. If not provided, a secure secret is auto-generated.

Handling Incoming Events

Your endpoint receives POST requests with event payloads. Here’s how to handle them:

1from fastapi import FastAPI, Request, HTTPException
2
3app = FastAPI()
4
5@app.post("/webhooks/airweave")
6async def handle_webhook(request: Request):
7 event = await request.json()
8
9 match event["event_type"]:
10 case "sync.completed":
11 job_id = event["job_id"]
12 collection = event["collection_name"]
13 print(f"Sync completed: {collection} (job: {job_id})")
14 # Trigger your downstream workflow here
15
16 case "sync.failed":
17 error = event.get("error", "Unknown error")
18 collection = event["collection_name"]
19 print(f"Sync failed: {collection} - {error}")
20 # Send alert or log the failure
21
22 case "sync.running":
23 print(f"Sync started: {event['collection_name']}")
24
25 return {"status": "ok"}

Respond quickly! Return 200 OK immediately and process the event asynchronously. If your endpoint takes too long (30s timeout), the delivery will be marked as failed and retried.

Signature Verification

Every webhook delivery is signed. Verify signatures to ensure requests actually come from Airweave and haven’t been tampered with.

Get Your Signing Secret

First, retrieve your subscription’s signing secret:

$curl -X GET 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}?include_secret=true' \
> -H 'x-api-key: YOUR_API_KEY'

The response includes a secret field in the format whsec_.... Keep this secret secure.

Verify the Signature

Events include three headers for verification:

HeaderDescription
svix-idUnique message identifier
svix-timestampUnix timestamp of delivery
svix-signatureHMAC-SHA256 signature
1import hmac
2import hashlib
3import base64
4
5def verify_signature(payload: bytes, headers: dict, secret: str) -> bool:
6 msg_id = headers.get("svix-id")
7 timestamp = headers.get("svix-timestamp")
8 signature = headers.get("svix-signature")
9
10 if not all([msg_id, timestamp, signature]):
11 return False
12
13 # Decode the secret (remove whsec_ prefix)
14 secret_bytes = base64.b64decode(secret.replace("whsec_", ""))
15
16 # Build the signed content
17 signed_content = f"{msg_id}.{timestamp}.{payload.decode()}"
18
19 # Compute expected signature
20 expected = hmac.new(
21 secret_bytes,
22 signed_content.encode(),
23 hashlib.sha256
24 ).digest()
25 expected_b64 = base64.b64encode(expected).decode()
26
27 # Compare against provided signatures
28 for sig in signature.split(","):
29 sig_value = sig.split(" ")[-1] # Handle "v1,..." format
30 if hmac.compare_digest(expected_b64, sig_value):
31 return True
32
33 return False

Recommended: Use the official Svix verification libraries in production. They handle edge cases, timestamp validation, and replay attack prevention automatically.

Managing Subscriptions

List All Subscriptions

$curl -X GET 'https://api.airweave.ai/webhooks/subscriptions' \
> -H 'x-api-key: YOUR_API_KEY'

Get a Specific Subscription

$curl -X GET 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}' \
> -H 'x-api-key: YOUR_API_KEY'

This also returns recent delivery attempts for debugging.

Update a Subscription

Change the URL, event types, or disable/enable delivery:

$curl -X PATCH 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}' \
> -H 'x-api-key: YOUR_API_KEY' \
> -H 'Content-Type: application/json' \
> -d '{
> "event_types": ["sync.completed", "sync.failed", "sync.running"],
> "disabled": false
> }'

Disable a Subscription

Temporarily pause delivery without deleting:

$curl -X PATCH 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}' \
> -H 'x-api-key: YOUR_API_KEY' \
> -H 'Content-Type: application/json' \
> -d '{"disabled": true}'

Enable a Subscription

Re-enable a disabled subscription, optionally recovering missed messages:

$curl -X PATCH 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}' \
> -H 'x-api-key: YOUR_API_KEY' \
> -H 'Content-Type: application/json' \
> -d '{
> "disabled": false,
> "recover_since": "2025-01-15T00:00:00Z"
> }'

The recover_since parameter is optional. If provided, Airweave will retry all failed messages from that timestamp.

Delete a Subscription

Permanently remove a subscription:

$curl -X DELETE 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}' \
> -H 'x-api-key: YOUR_API_KEY'

This action cannot be undone. Pending deliveries will be cancelled.

Retrieving Message History

Get All Messages

Retrieve recent webhook messages sent to your organization:

$curl -X GET 'https://api.airweave.ai/webhooks/messages' \
> -H 'x-api-key: YOUR_API_KEY'

Filter by Event Type

$curl -X GET 'https://api.airweave.ai/webhooks/messages?event_types=sync.completed&event_types=sync.failed' \
> -H 'x-api-key: YOUR_API_KEY'

Get a Specific Message

$curl -X GET 'https://api.airweave.ai/webhooks/messages/{message_id}' \
> -H 'x-api-key: YOUR_API_KEY'

Include Delivery Attempts

See exactly what happened during delivery:

$curl -X GET 'https://api.airweave.ai/webhooks/messages/{message_id}?include_attempts=true' \
> -H 'x-api-key: YOUR_API_KEY'

Recovering Failed Messages

If your endpoint was down or had issues, you can replay failed messages:

$curl -X POST 'https://api.airweave.ai/webhooks/subscriptions/{subscription_id}/recover' \
> -H 'x-api-key: YOUR_API_KEY' \
> -H 'Content-Type: application/json' \
> -d '{
> "since": "2025-01-15T00:00:00Z",
> "until": "2025-01-16T00:00:00Z"
> }'
ParameterRequiredDescription
sinceYesStart of recovery window (inclusive)
untilNoEnd of recovery window (defaults to now)

This triggers a recovery task that replays failed messages in chronological order.

Best Practices

Return 200 OK immediately after receiving a webhook. Queue the event for background processing rather than doing heavy work inline. This prevents timeouts and ensures reliable delivery.

Webhooks may be delivered more than once. Use the event_id or job_id field to deduplicate. Store processed event IDs and skip duplicates.

Don’t skip signature verification. It protects against:

  • Spoofed requests from attackers
  • Replay attacks (Svix timestamps help prevent this)
  • Data tampering in transit

Webhook URLs must use HTTPS in production. This ensures the payload is encrypted in transit. HTTP is only allowed for local development.

Log incoming payloads, processing results, and any errors. This makes debugging much easier when something goes wrong.

Even if you only care about sync.completed, your handler should gracefully ignore unknown event types rather than failing.

Sample Application

A complete sample application demonstrating webhook handling, signature verification, and event processing is coming soon.

Next Steps