VERINODE|API

Verinode Developer

Webhooks

Receive events in real time.

Register an HTTPS endpoint to receive events as they happen. Requires the webhooks scope; the signing secret is returned once. Up to 10 endpoints per operator — an eleventh returns 409 too_many_endpoints.

curl -X POST https://api.verinode.ai/v1/webhook-endpoints \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{ "url": "https://example.com/hooks", "events": ["job.created","export.completed"] }'
→ 201 { "id": "…", "secret": "whsec_…", "status": "active" }

#Event catalog

Five event types fire today. The list is deliberately exactly what has a live producer — you can never subscribe to something that never arrives. Subscribe with ["*"] to receive all of them.

job.createdA job was created.
client.createdA client was created.
ingestion.completedA document you pushed finished extracting.
ingestion.failedA document you pushed failed extraction.
export.completedAn export is ready to download.

#Only what you pushed

ingestion.completed and ingestion.failed fire only for documents ingested through POST /v1/documents. A document the operator emailed in or uploaded through the Verinode dashboard fires nothing — it never reaches your endpoint.

This is a privacy boundary, not a gap: the operator's own paperwork is their business, and a partner integration is not entitled to a feed of it. Design for it. If you need to know about documents you didn't send, poll the read endpoints with updated_since — the records that extraction produces show up there like any other change.

#Verifying signatures

Each delivery carries Verinode-Signature: t=<unix>,v1=<hmac>. Recompute HMAC_SHA256(secret, "<t>.<raw body>") and compare:

import hmac, hashlib
def verify(secret, body, header):
    parts = dict(p.split("=") for p in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.{body}".encode(),
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

#Delivery

Deliveries retry on failure (1m → 24h) and an endpoint failing for 7 straight days auto-disables. Only public HTTPS URLs are accepted (validated at registration and at every delivery). Can't run a listener? Poll GET /v1/outbound-events instead.