Docira

Reference

Webhooks

Docira can POST a signed JSON event to your endpoint when a parse or batch completes. Register your URL in the dashboard, then verify the X-Docira-Signature header before acting on the payload.

Event types

Four events are emitted. Subscribe to all or a subset in the dashboard.

EventWhen fireddata field
parse.completedSingle-page or multi-page parse finishesParseResponse
parse.failedA parse request exhausts all retries{ request_id, error, status_code }
batch.completedAll documents in a batch are resolvedBatchStatusResponse
batch.failedA batch is permanently failed{ batch_id, error, failed_documents }

Envelope format

Every webhook is a POST with Content-Type: application/json. The body is a fixed envelope — the event-specific payload lives under data.

Request headers
X-Docira-Event:      parse.completed
X-Docira-Request-ID: req_01hwzqj2k8fgp7e3n6mdct0yxv
X-Docira-Signature:  sha256=6a8f0c3d…
Content-Type:        application/json
Body (envelope)
{
  "event":      "parse.completed",
  "request_id": "req_01hwzqj2k8fgp7e3n6mdct0yxv",
  "timestamp":  1714000000.123,
  "data": {
    // ParseResponse or BatchStatusResponse depending on event
  }
}

timestamp is a Unix epoch float (seconds). Use it to reject events older than a reasonable window (5 minutes is typical) as a replay defence.

Verifying signatures

When a webhook secret is configured, Docira signs the raw request body with HMAC-SHA256 and sets X-Docira-Signature: sha256=<hex>. Always verify against the raw bytes before JSON-parsing, and use a constant-time comparison to prevent timing attacks.

Set a webhook secret. Endpoints without a secret receive unsigned requests. Anyone who discovers your URL can send forged events. Always configure a secret in the dashboard before accepting webhook data.

Verify in your handler
import hashlib
import hmac

def verify_signature(
    raw_body: bytes,
    signature_header: str,
    secret: str,
) -> bool:
    """Return True if the X-Docira-Signature header is valid."""
    if not signature_header.startswith("sha256="):
        return False
    expected = hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    received = signature_header.removeprefix("sha256=")
    return hmac.compare_digest(expected, received)

# In a FastAPI/Flask handler:
# raw = await request.body()
# sig = request.headers.get("X-Docira-Signature", "")
# if not verify_signature(raw, sig, WEBHOOK_SECRET):
#     raise HTTPException(status_code=401, detail="Invalid signature")

The signature covers the exact bytes Docira sent. Middleware that re-encodes or compresses the body will break verification — make sure your framework exposes the raw body before any JSON parsing.

Idempotency

The same event may arrive more than once. Retries after a network timeout deliver identical envelopes with the same request_id. Your handler must be idempotent.

  • Use request_id as a deduplication key. Store processed IDs in your database or a Redis set with a TTL of 24 hours. Return 200 for duplicate events without re-processing them.
  • batch.completed fires once — when all documents resolve. It does not fire per-document; listen to parse.completed if you need per-document notifications.
  • Events are not guaranteed to arrive in order. A parse.completed for page 3 may arrive before page 1. Sort by timestamp after collecting all events for a job.

Failure and retries

Docira retries a failed delivery up to 3 times with exponential backoff (base 2 s, no jitter cap). A delivery is considered failed if your endpoint returns a 4xx or 5xx status, or does not respond within 30 seconds.

AttemptDelay before retryCumulative wait
1 (initial)0 s
22 s~2 s
34 s~6 s
4 (final)8 s~14 s

After 3 retries Docira marks the delivery as permanently failed and moves on. Failed deliveries appear in the webhook log where you can manually replay them.

Auto-pause threshold

If your endpoint records 5 consecutive final failures (all retries exhausted), Docira pauses delivery for that endpoint and emails the account owner. Re-enable in the dashboard once your receiver is healthy. Events that arrived during the pause are not replayed automatically — replay each one manually from the webhook log if needed.

Test events

Send a synthetic event to any registered endpoint from the dashboard to confirm your handler is wired correctly before routing live traffic.

  1. Go to Dashboard → Webhooks.
  2. Select an endpoint and choose an event type from the dropdown.
  3. Click Send test event. The dashboard shows the raw response from your server within 5 seconds.

Test events carry a real signature computed against your secret, so your verification logic runs end-to-end. The data field contains a minimal fixture — not a real parse result.

Next steps

Ready to integrate? Read the API docs →