Webhooks

Receive a callback when processing completes or fails.

Instead of polling, you can provide a webhook_url when submitting a document. Kita will POST to your URL when processing completes or fails.

Sequence Diagram

Your Server                   Kita API                    Kita Worker
    │                            │                            │
    │  POST /api/v1/documents    │                            │
    │  { file, document_type,    │                            │
    │    webhook_url }           │                            │
    │ ─────────────────────────► │                            │
    │                            │  Queue job (pg-boss)       │
    │  202 Accepted              │ ──────────────────────────►│
    │  { document_id, job_id,    │                            │
    │    status: "pending" }     │                            │
    │ ◄───────────────────────── │                            │
    │                            │                            │
    │   (no need to poll)        │                            │ OCR + Extract
    │                            │                            │ + Validate
    │                            │                            │ + Fraud Check
    │                            │                            │
    │                            │                            │ Processing done
    │  POST webhook_url          │                            │
    │  { event, document_id,     │                            │
    │    status }                │ ◄──────────────────────────│
    │ ◄──────────────────────────│                            │
    │                            │                            │
    │  GET /api/v1/documents/:id │                            │
    │ ─────────────────────────► │  (fetch full result)       │
    │  { result: { ... } }       │                            │
    │ ◄───────────────────────── │                            │

Webhook Payload (Success)

{
  "event": "document.completed",
  "document_id": 42,
  "status": "completed",
  "document_type": "bank_statement",
  "file_name": "statement.pdf",
  "processing_time_seconds": 12.4,
  "completed_at": "2025-01-15T10:30:00.000Z"
}

Webhook Payload (Failure)

{
  "event": "document.failed",
  "document_id": 42,
  "status": "failed",
  "error": "Processing error description",
  "error_category": "processing_error",
  "document_type": "bank_statement",
  "file_name": "statement.pdf",
  "failed_at": "2025-01-15T10:30:00.000Z"
}

Webhook Behavior

  • Delivery: Fire-and-forget HTTP POST with 10-second timeout
  • Retry: No automatic retries — if your server is down, the webhook is lost. Use polling as a fallback.
  • Security: Webhooks are sent over HTTP or HTTPS. Use HTTPS in production.
  • Body: JSON with Content-Type: application/json
  • Your endpoint should return any 2xx status. Non-2xx responses are logged but not retried.

Example: S3 Upload with Webhook

from kita import KitaClient
 
client = KitaClient(api_key="kita_prod_...")
 
# Submit from S3 presigned URL — no polling needed
result = client.process_url(
    "https://my-bucket.s3.amazonaws.com/statement.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
    "bank_statement",
    wait=False,
    webhook_url="https://your-server.com/kita-webhook"
)
print(f"Submitted: document_id={result.document_id}")
# Your webhook endpoint will receive a POST when done
# cURL equivalent
curl -X POST https://api.kita.ai/api/v1/documents \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file_url": "https://my-bucket.s3.amazonaws.com/statement.pdf?X-Amz-Algorithm=...",
    "document_type": "bank_statement",
    "webhook_url": "https://your-server.com/kita-webhook"
  }'

Webhook Receiver Example

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/kita-webhook", methods=["POST"])
def kita_webhook():
    payload = request.json
    if payload["event"] == "document.completed":
        # Fetch full results
        doc_id = payload["document_id"]
        print(f"Document {doc_id} completed in {payload['processing_time_seconds']}s")
        # Use the API to get the full result:
        # GET /api/v1/documents/{doc_id}
    elif payload["event"] == "document.failed":
        print(f"Document {payload['document_id']} failed: {payload['error']}")
    return "", 200

On this page