Skip to main content
Webhooks allow you to receive real-time notifications when events occur in Airweave. Instead of polling the API, register an endpoint and Airweave will push updates to you the moment they happen.
Beta FeatureThe Webhooks API is currently in beta. The API is stable but may receive enhancements based on feedback.

Overview

Webhooks are HTTP POST requests sent to your server when specific events occur:
  • Sync jobs completing or failing
  • Source connections being created, authenticated, or deleted
  • Collections being created, updated, or deleted
This enables reactive integrations: trigger workflows, update dashboards, send alerts, or sync state with external systems automatically.
┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Airweave  │ ──── │    Svix     │ ──── │ Your Server │
│  (Events)   │      │  (Delivery) │      │  (Webhook)  │
└─────────────┘      └─────────────┘      └─────────────┘
Airweave uses Svix for webhook delivery, which provides:
  • Automatic retries with exponential backoff
  • Cryptographic signature verification
  • Delivery guarantees and observability

Available Event Types

Sync Events

EventDescriptionWhen it fires
sync.pendingSync job queuedJob created and waiting to start
sync.runningSync job startedJob begins processing
sync.completedSync finished successfullyAll data synced without errors
sync.failedSync job failedJob encountered an error
sync.cancelledSync job cancelledJob was manually cancelled

Source Connection Events

EventDescriptionWhen it fires
source_connection.createdConnection createdNew source connection added
source_connection.auth_completedOAuth completedConnection authenticated
source_connection.deletedConnection removedConnection and data deleted

Collection Events

EventDescriptionWhen it fires
collection.createdCollection createdNew collection set up
collection.updatedCollection modifiedName or config changed
collection.deletedCollection removedCollection and data deleted
Most integrations only need sync.completed and sync.failed. Subscribe to other events if you need to track infrastructure changes or audit activities.

Creating a Subscription

Create a webhook subscription to start receiving events.
1

Prepare your endpoint

Set up an HTTPS endpoint that accepts POST requests and returns a 200 status code. Use ngrok or similar tools for local development.
2

Create subscription

Use the API to register your endpoint and choose which events to receive.
3

Verify signatures

Validate webhook signatures to ensure requests are from Airweave.
from airweave import AirweaveSDK

client = AirweaveSDK(api_key="YOUR_API_KEY")

# Create webhook subscription
subscription = client.webhooks.create_subscription(
    url="https://api.mycompany.com/webhooks/airweave",
    event_types=["sync.completed", "sync.failed"]
)

print(f"Subscription ID: {subscription.id}")
print(f"URL: {subscription.url}")
print(f"Events: {subscription.filter_types}")

# Save the signing secret for signature verification
detail = client.webhooks.get_subscription(
    subscription_id=subscription.id,
    include_secret=True
)
signing_secret = detail.secret
print(f"Secret: {signing_secret}")
# Store this securely! You'll need it to verify signatures.

Request Parameters

url
string
required
HTTPS URL where webhook events will be delivered. Must be publicly accessible and return a 2xx status code.Example: "https://api.mycompany.com/webhooks/airweave"
event_types
array
required
List of event types to subscribe to. Events not in this list won’t be delivered.Available: sync.pending, sync.running, sync.completed, sync.failed, sync.cancelled, source_connection.created, source_connection.auth_completed, source_connection.deleted, collection.created, collection.updated, collection.deletedExample: ["sync.completed", "sync.failed"]
secret
string
Optional custom signing secret (min 24 characters). If not provided, a secure secret will be auto-generated.Example: "whsec_C2FVsBQIhrscChlQIMV10R9X4jZ8"

Webhook Payloads

sync.completed

Fired when a sync job finishes successfully:
{
  "event_type": "sync.completed",
  "sync_id": "440e8400-e29b-41d4-a716-446655440099",
  "sync_job_id": "550e8400-e29b-41d4-a716-446655440000",
  "collection_id": "770e8400-e29b-41d4-a716-446655440002",
  "collection_readable_id": "sales-data-ab123",
  "collection_name": "Sales Data",
  "source_connection_id": "660e8400-e29b-41d4-a716-446655440001",
  "source_type": "salesforce",
  "entities_inserted": 42,
  "entities_updated": 10,
  "entities_deleted": 3,
  "entities_skipped": 120,
  "chunks_written": 215,
  "timestamp": "2025-01-15T14:30:00Z"
}
event_type
string
Always "sync.completed"
sync_job_id
UUID
Unique identifier for this specific sync job run
collection_readable_id
string
Human-readable collection ID (e.g., "sales-data-ab123")
source_type
string
Source connector type (e.g., "salesforce", "github", "notion")
entities_inserted
integer
Number of new entities added
entities_updated
integer
Number of existing entities updated
entities_deleted
integer
Number of entities removed
chunks_written
integer
Number of chunks written to vector database

sync.failed

Fired when a sync job encounters an error:
{
  "event_type": "sync.failed",
  "sync_id": "440e8400-e29b-41d4-a716-446655440099",
  "sync_job_id": "550e8400-e29b-41d4-a716-446655440000",
  "collection_id": "770e8400-e29b-41d4-a716-446655440002",
  "collection_readable_id": "sales-data-ab123",
  "collection_name": "Sales Data",
  "source_connection_id": "660e8400-e29b-41d4-a716-446655440001",
  "source_type": "salesforce",
  "error": "Authentication token expired",
  "timestamp": "2025-01-15T14:02:30Z"
}

source_connection.created

Fired when a new source connection is added:
{
  "event_type": "source_connection.created",
  "source_connection_id": "660e8400-e29b-41d4-a716-446655440001",
  "collection_readable_id": "finance-data-ab123",
  "source_type": "notion",
  "is_authenticated": false,
  "timestamp": "2025-01-15T13:55:00Z"
}

collection.created

Fired when a new collection is created:
{
  "event_type": "collection.created",
  "collection_id": "770e8400-e29b-41d4-a716-446655440002",
  "collection_readable_id": "customer-support-x7k9m",
  "collection_name": "Customer Support",
  "timestamp": "2025-01-15T10:00:00Z"
}

Verifying Signatures

Every webhook delivery includes signature headers for verification. Always verify signatures to ensure requests are from Airweave.

Request Headers

POST /webhooks/airweave HTTP/1.1
Host: your-server.com
Content-Type: application/json
svix-id: msg_2xKvB8LPqM4nRst
svix-timestamp: 1705329000
svix-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=
svix-id
string
Unique message identifier
svix-timestamp
integer
Unix timestamp when the message was sent
svix-signature
string
Space-separated list of signatures (format: v1,<base64-signature>)

Verification Example

import hmac
import hashlib
import base64
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

SIGNING_SECRET = "whsec_C2FVsBQIhrscChlQIMV10R9X4jZ8"  # From subscription

def verify_signature(payload: bytes, headers: dict) -> bool:
    """Verify Svix webhook signature."""
    msg_id = headers.get("svix-id")
    timestamp = headers.get("svix-timestamp")
    signatures = headers.get("svix-signature", "")
    
    if not msg_id or not timestamp or not signatures:
        return False
    
    # Decode the secret (remove 'whsec_' prefix)
    secret = SIGNING_SECRET
    if secret.startswith("whsec_"):
        secret = secret[6:]
    secret_bytes = base64.b64decode(secret)
    
    # Construct signed content
    signed_content = f"{msg_id}.{timestamp}.{payload.decode('utf-8')}"
    
    # Compute expected signature
    expected = hmac.new(
        secret_bytes,
        signed_content.encode('utf-8'),
        hashlib.sha256
    ).digest()
    expected_b64 = base64.b64encode(expected).decode('utf-8')
    
    # Compare with provided signatures
    for sig in signatures.split(" "):
        parts = sig.split(",", 1)
        if len(parts) == 2 and parts[0] == "v1":
            if hmac.compare_digest(expected_b64, parts[1]):
                return True
    
    return False

@app.post("/webhooks/airweave")
async def receive_webhook(request: Request):
    payload = await request.body()
    headers = dict(request.headers)
    
    # Verify signature
    if not verify_signature(payload, headers):
        raise HTTPException(status_code=401, detail="Invalid signature")
    
    # Parse event
    event = await request.json()
    event_type = event.get("event_type")
    
    if event_type == "sync.completed":
        print(f"Sync completed: {event['collection_name']}")
        print(f"  Inserted: {event['entities_inserted']}")
        print(f"  Updated: {event['entities_updated']}")
        # Trigger your workflow here
        
    elif event_type == "sync.failed":
        print(f"Sync failed: {event['collection_name']}")
        print(f"  Error: {event['error']}")
        # Send alert
    
    return {"status": "ok"}
Always verify signatures before processing webhook payloads. This prevents attackers from sending forged requests to your endpoint.

Managing Subscriptions

List Subscriptions

subscriptions = client.webhooks.list_subscriptions()

for sub in subscriptions:
    print(f"ID: {sub.id}")
    print(f"URL: {sub.url}")
    print(f"Events: {sub.filter_types}")
    print(f"Status: {'Disabled' if sub.disabled else 'Active'}")
    print(f"Health: {sub.health_status}")
    print()

Update Subscription

# Change URL
updated = client.webhooks.update_subscription(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234",
    url="https://api.mycompany.com/webhooks/airweave-v2"
)

# Change event types
updated = client.webhooks.update_subscription(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234",
    event_types=["sync.completed", "sync.failed", "sync.running"]
)

# Disable subscription
updated = client.webhooks.update_subscription(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234",
    disabled=True
)

# Re-enable and recover missed messages
from datetime import datetime, timezone, timedelta

updated = client.webhooks.update_subscription(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234",
    disabled=False,
    recover_since=(datetime.now(timezone.utc) - timedelta(days=7))
)

Delete Subscription

deleted = client.webhooks.delete_subscription(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234"
)

print(f"Deleted: {deleted.url}")
Once deleted, Airweave will stop sending events to this endpoint immediately. Any pending deliveries will be cancelled.

Recovering Failed Messages

If your endpoint was temporarily down, you can replay failed messages.
from datetime import datetime, timezone, timedelta

# Recover last 24 hours
recovery = client.webhooks.recover_messages(
    subscription_id="c3d4e5f6-a7b8-9012-cdef-345678901234",
    since=datetime.now(timezone.utc) - timedelta(days=1),
    until=datetime.now(timezone.utc)
)

print(f"Recovery task ID: {recovery.id}")
print(f"Status: {recovery.status}")  # "running" or "completed"

Viewing Messages and Attempts

Inspect webhook messages and delivery attempts for debugging.
# List all messages
messages = client.webhooks.list_messages(
    event_types=["sync.completed", "sync.failed"]
)

for msg in messages[:5]:
    print(f"Event: {msg.event_type}")
    print(f"Timestamp: {msg.timestamp}")
    print(f"Payload: {msg.payload}")
    print()

# Get specific message with delivery attempts
message = client.webhooks.get_message(
    message_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    include_attempts=True
)

print(f"Event: {message.event_type}")
if message.delivery_attempts:
    for attempt in message.delivery_attempts:
        print(f"  Attempt {attempt.id}:")
        print(f"    Status: {attempt.status}")
        print(f"    Response Code: {attempt.response_status_code}")
        print(f"    Response: {attempt.response}")

Health Status

Subscriptions have health statuses based on recent delivery attempts:
All recent deliveries succeeded (2xx responses). Your endpoint is working correctly.
Mix of successes and failures in recent deliveries. Check your endpoint logs for intermittent issues.
Multiple consecutive failures beyond threshold (3+). Action required - your endpoint is down or returning errors.
No delivery attempts yet. This is normal for newly created subscriptions.

Best Practices

1

Always verify signatures

Use the signing secret to validate that requests are from Airweave. Never skip signature verification in production.
2

Respond quickly

Return a 200 OK response as soon as you receive the webhook. Process the event asynchronously if needed.
3

Handle retries

Make your endpoint idempotent. Airweave may deliver the same event multiple times during retries.
4

Monitor health status

Check subscription health regularly. Set up alerts for failing status.
5

Use HTTPS

Webhook URLs must use HTTPS. Use ngrok or similar tools for local development.

Use Cases

Trigger Workflows

Start a data pipeline, refresh a cache, or kick off downstream processing when a sync completes.

Send Alerts

Notify your team via Slack, email, or PagerDuty when a sync fails or a connection is removed.

Track Infrastructure

Know the moment a source connection is added, authenticated, or deleted, or when collections change.

Audit & Logging

Record every lifecycle event to your logging system for compliance or debugging.

Next Steps

Collections

Learn about organizing data with collections

Search

Search across your synced data