Receive real-time notifications when pipeline executions and agent runs complete

Webhooks


Webhooks let your application receive push notifications instead of polling for execution status. When you start a pipeline run or agent run, supply a webhook_url in the request body. The platform queues a delivery after the execution completes.

Registering a webhook URL

Pass webhook_url in the run request:

# Pipeline run
curl -X POST https://api.9thsense.ai/v1/pipeline/run \
  -H "X-Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context_id": "passport_extraction",
    "tenant_id": "acme-corp",
    "input": {"filename": "passport.jpg", "data": "<base64>"},
    "sync": false,
    "webhook_url": "https://your-app.example.com/webhooks/9thsense"
  }'

# Agent run
curl -X POST https://api.9thsense.ai/v1/agent/run \
  -H "X-Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "goal": "thailand_visa",
    "tenant_id": "acme-corp",
    "documents": [{"filename": "passport.jpg", "data": "<base64>", "mime_type": "image/jpeg"}],
    "sync": false,
    "webhook_url": "https://your-app.example.com/webhooks/9thsense"
  }'

webhook_url is only used when sync is false. Synchronous runs return the result directly in the HTTP response.

Delivery

The webhook worker polls the webhook_outbox table every 15 seconds. When it finds pending entries it delivers up to 10 at a time by POSTing the payload as JSON:

POST https://your-app.example.com/webhooks/9thsense
Content-Type: application/json
User-Agent: 9thSense-Webhooks/1.0

Your endpoint must return a 2xx status code within 30 seconds. Any other response (non-2xx, timeout, or connection error) is treated as a failure.

Payload structure

The webhook body is the execution result — the same JSON you would receive from a synchronous run or from polling GET /v1/pipeline/executions/{execution_id}:

{
  "execution_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "context_id": "passport_extraction",
  "status": "completed",
  "tenant_id": "acme-corp",
  "result": {
    "full_name": "Rahul Sharma",
    "date_of_birth": "1990-03-15",
    "passport_number": "K1234567",
    "nationality": "INDIA",
    "date_of_expiry": "2030-03-14"
  },
  "steps": [
    {"tool": "extract_fields", "order": 0, "status": "ok", "duration_ms": 1240}
  ],
  "total_ms": 1350,
  "started_at": "2026-03-25T09:00:00Z",
  "completed_at": "2026-03-25T09:00:01.350Z"
}

Retry policy

The platform retries failed deliveries automatically:

AttemptBehaviour
1–4Status set back to pending; retried on the next worker poll cycle
5Status set to failed; no further attempts

The worker polls every 15 seconds (webhook_worker_interval = 15). The maximum number of attempts before a webhook is permanently failed is 5 (webhook_max_attempts = 5).

Entries in the webhook_outbox table cycle through statuses: pendingdelivered or pending → (after 5 attempts) failed.

Receiving webhooks

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhooks/9thsense")
async def receive_webhook(request: Request):
    payload = await request.json()
    execution_id = payload.get("execution_id")
    status = payload.get("status")
    result = payload.get("result", {})

    if status == "completed":
        # Process extraction results
        print(f"Execution {execution_id} completed: {result}")
    elif status == "failed":
        print(f"Execution {execution_id} failed")

    # Must return 2xx or the platform will retry
    return {"received": True}

Keep your endpoint fast. The delivery client waits up to 30 seconds. If your handler needs to do slow work (database writes, downstream calls), acknowledge immediately and process asynchronously:

import asyncio

@app.post("/webhooks/9thsense")
async def receive_webhook(request: Request, background_tasks):
    payload = await request.json()
    background_tasks.add_task(process_result, payload)
    return {"received": True}  # return immediately

Webhook delivery stats

Query delivery statistics for the last N days:

curl "https://api.9thsense.ai/v1/analytics/webhooks?days=7" \
  -H "X-Api-Key: $API_KEY"

Response:

{
  "delivered": 482,
  "failed": 3,
  "pending": 1,
  "total": 486
}

Requires the read scope.

Debugging failed webhooks

If deliveries are failing, check:

  1. Reachability — your endpoint must be publicly accessible. localhost URLs will never succeed.
  2. Response code — ensure your handler returns a 2xx status. Redirects (3xx) are not followed.
  3. Timeout — the delivery client has a 30-second timeout. Acknowledge before doing slow processing.
  4. Stats — use GET /v1/analytics/webhooks to check whether the platform is attempting delivery.