VERINODE|API

Verinode Developer

API reference

Every endpoint, grouped by area. The machine-readable contract is at /openapi.

Meta

Connectivity and key introspection. Server-side only: the API sends no CORS headers, so a browser fetch cannot call it. Every authenticated response carries RateLimit-Limit / -Remaining / -Reset; 401, 403, and the pre-auth per-IP 429 carry none. Every response carries Verinode-Request-Id.

#GET/v1/ping

Authenticates the key and echoes its resolved identity, the fastest way to confirm setup. Requires no scope, so it is also the right call to isolate a 403.

Responses

200The key is valid; returns operator_id, mode, scopes, sandbox.
401Missing or malformed key.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/ping

#GET/v1/api-keys

Returns key metadata for the operator this key resolves to. Secrets are never returned, only the prefix and status. Note the boundary: a test key resolves to the sandbox operator, so it lists only sandbox keys, never your live ones. Minting stays in the dashboard (admin-only).

Scope keys

Responses

200{ data: [ApiKey] }
403Missing the keys scope.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/api-keys

Documents

Push documents through the extraction pipeline and poll results.

#POST/v1/documents

Uploads a document for extraction. Asynchronous: returns 202 with an ingestion id. Send multipart form-data with a file, or JSON with filename + base64 content_base64. 300/hour; test keys are additionally capped at 25 extractions/day.

Scope ingest3 IU on live keys, charged at accept (202), not at extraction and not refunded on failure; free on test

Parameters

filebinary · bodyMultipart file part (or use the JSON lane below).
filenamerequiredstring · bodyREQUIRED on the JSON lane (omit it and the call 400s). Carried by the file part on multipart.
content_base64string · bodyBase64 file bytes (JSON alternative to multipart). Requires filename.
content_typestring · bodyOptional; inferred from the filename extension when omitted.
Idempotency-Keystring · headerHonored here. Makes a retry safe: replays the first response instead of charging and extracting twice.

Responses

202Accepted for extraction; returns { id, status }. IUs are already spent at this point.
400Missing file, or JSON body missing filename / content_base64.
402Intelligence Units exhausted; nothing was queued.
413Over the 25 MB cap.
415Unsupported content type.
429Over 300/hour, or over the test-key 25/day cap (sandbox_quota).
curl -X POST https://api.verinode.ai/v1/documents \
  -H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
  -F file=@invoice.pdf

#GET/v1/ingestions/{id}

Returns status and, once complete, the humanized extraction receipt (what was detected, saved, and normalized). Poll while status is `processing`, the only non-terminal state. `canceled` means the operator aborted it in the dashboard and it will never be processed: stop polling and re-send if you still need it.

Scope ingest

Parameters

idrequireduuid · pathThe ingestion id.

Responses

200{ status: processing | completed | failed | canceled, receipt, error }.
404No ingestion with that id.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/ingestions/<id>

Events

Import activity events to feed process mining. Free (no Intelligence Units).

#POST/v1/events

Records one activity event onto Verinode's process model. Case resolves by job_id or claim_number. Idempotency-Key is IGNORED here: dedupe on external_id instead. 5,000/hour.

Scope write

Parameters

activityrequiredstring · bodyWhat happened (e.g. job_completed).
occurred_atrequireddate-time · bodyWhen it happened (ISO 8601 UTC). Not in the future, not over 10 years old.
caseobject · bodyOptional. { job_id } or { claim_number }; omit or null to record an event unattached to a job.
domainstring · bodyOne of vendor, field, client, billing, team. Defaults to field.
external_idstring · bodyYour stable id. Re-sending it is a no-op (duplicate). WITHOUT it, a retry writes the event AGAIN.

Responses

201Recorded; returns { id, status: created }.
200Already seen (external_id match); returns { id: null, status: duplicate }.
400Validation failed (activity, occurred_at, domain, attributes).
422Case reference could not resolve, or matched more than one job.
curl -X POST https://api.verinode.ai/v1/events \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{ "activity":"job_completed","occurred_at":"2026-07-01T16:30:00Z","case":{"job_id":"…"} }'

#POST/v1/events/batch

Bulk-import events. Returns a per-item result envelope so partial failures are visible. Always inspect it: a 200 does not mean all 1,000 landed. Idempotency-Key is IGNORED: give every event an external_id, or a retried batch double-writes each one that lacks it. 500 calls/hour.

Scope write

Parameters

eventsrequiredarray · bodyArray of event objects (max 1,000).

Responses

200Per-item results { accepted, rejected, results }. Each result carries an id (null when duplicate) or an error.
413More than 1,000 events in the batch. Split the backfill.
429Over 500 batch calls this hour.
curl -X POST https://api.verinode.ai/v1/events/batch \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{ "events": [ … ] }'

Records

Create and read structured records directly.

#POST/v1/jobs

Create a job without a source document. Fields pass through the schema validator; operator_id, id, source, and timestamps are server-owned. Returns the created object, serialized exactly as its list row. 300/hour.

Scope write

Parameters

Idempotency-Keystring · headerHonored here. Replays the first response instead of creating a second record. Reusing a key with a different body returns 422.

Responses

201The created job.
400Validation failed.
422Idempotency-Key reused with a different body.
curl -X POST https://api.verinode.ai/v1/jobs \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{ "client_name":"Riverside Restoration","rcv_total":"18450.00" }'

#GET/v1/jobs

Cursor-paginated over (updated_at, id). Each enum value ships a humanized _label; money as decimal strings. Sync is at-least-once, so upsert by id. 600/hour.

Scope read

Parameters

limitinteger · query1–100, default 25.
cursorstring · queryFrom a previous next_cursor. Pass it back verbatim; never construct one.
updated_sincedate-time · queryInclusive. Pass back the synced_through you stored to fetch only what changed.
created_afterdate-time · queryExclusive, on created_at. New records only. Never shows an edit to a record you already have.

Responses

200{ data, next_cursor, has_more, synced_through }. synced_through is non-null only on the last page.
400Malformed cursor or timestamp.
curl -H "Authorization: Bearer $KEY" \
  "https://api.verinode.ai/v1/jobs?limit=100&updated_since=2026-07-17T09:30:00Z"

#GET/v1/jobs/{id}

One job, serialized identically to its list row. A record that does not exist and one owned by another operator return the same 404, deliberately. 1,000/hour.

Scope read

Parameters

idrequireduuid · pathThe job id.

Responses

200The job.
404No job with that id for this operator.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/jobs/<id>

#POST/v1/clients

Create a client; PII fields are encrypted at rest on write. Returns the created object. Free. 300/hour.

Scope write

Parameters

Idempotency-Keystring · headerHonored here. Replays the first response instead of creating a second record.

Responses

201The created client.
400Validation failed.
422Idempotency-Key reused with a different body.
curl -X POST https://api.verinode.ai/v1/clients \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{ "name":"Acme Property Management","contact_email":"dana@acmepm.example" }'

#GET/v1/clients

Cursor-paginated, contact fields decrypted. Same sync semantics as jobs. 600/hour.

Scope read

Parameters

limitinteger · query1–100, default 25.
cursorstring · queryPage cursor.
updated_sincedate-time · queryInclusive. Only what changed since.
created_afterdate-time · queryExclusive, on created_at.

Responses

200{ data, next_cursor, has_more, synced_through }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/clients"

#GET/v1/clients/{id}

One client, serialized identically to its list row. 1,000/hour.

Scope read

Parameters

idrequireduuid · pathThe client id.

Responses

200The client.
404No client with that id for this operator.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/clients/<id>

#GET/v1/vendors

Cursor-paginated. Read-only in v1. Your own vendor records only, nothing catalog- or peer-derived. 600/hour.

Scope read

Parameters

limitinteger · query1–100, default 25.
cursorstring · queryPage cursor.
updated_sincedate-time · queryInclusive. Only what changed since.
created_afterdate-time · queryExclusive, on created_at.

Responses

200{ data, next_cursor, has_more, synced_through }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/vendors"

#GET/v1/vendors/{id}

One vendor, serialized identically to its list row. 1,000/hour.

Scope read

Parameters

idrequireduuid · pathThe vendor id.

Responses

200The vendor.
404No vendor with that id for this operator.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/vendors/<id>

Export

Export your own data asynchronously.

#POST/v1/exports

Kick off an async full-account export. Omit resources for everything (jobs, vendors, clients). One at a time, 4 per rolling 24h, 20 calls/hour. Idempotency-Key is IGNORED here: the 409 is what makes a retry safe.

Scope read

Parameters

resourcesstring[] · bodyResource names, or omit for all.

Responses

202{ id, status: processing }.
400Unknown or empty resource list.
409An export is already in progress (export_in_progress).
429Past 4 exports in 24h (export_quota).
curl -X POST https://api.verinode.ai/v1/exports \
  -H "Authorization: Bearer $KEY" -d '{}'

#GET/v1/exports/{id}

Poll status, row counts, and the download URL. `processing` is the only non-terminal state (queued and running both read as processing, so this always agrees with the create response); it resolves to completed, failed, or expired.

Scope read

Parameters

idrequireduuid · pathThe export id.

Responses

200{ status, resource_counts, download_url }.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/exports/<id>

#GET/v1/exports/{id}/download

Streams the decrypted JSON bundle. Available while completed and unexpired (7-day TTL).

Scope read

Parameters

idrequireduuid · pathThe export id.

Responses

200The export artifact (application/json).
410Expired.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/exports/<id>/download -o export.json

Webhooks

Register endpoints to receive events in real time. Five event types fire today: job.created, client.created, ingestion.completed, ingestion.failed, export.completed. Ingestion events fire ONLY for documents pushed through POST /v1/documents. A document the operator emailed or uploaded themselves never reaches your endpoint.

#POST/v1/webhook-endpoints

Register an HTTPS endpoint. The signing secret is returned once. Only public HTTPS URLs are accepted (re-validated at every delivery). Max 10 endpoints per operator.

Scope webhooks

Parameters

urlrequiredstring · bodyYour HTTPS endpoint. Must resolve to a public address.
eventsstring[] · bodyEvent types to receive, or ["*"] for all five.
Idempotency-Keystring · headerHonored here.

Responses

201{ id, secret, status }.
400URL not public HTTPS.
409Already at 10 endpoints (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"] }'

#GET/v1/webhook-endpoints

Lists your registered endpoints (secrets excluded).

Scope webhooks

Responses

200{ data: [Endpoint] }.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/webhook-endpoints

#DELETE/v1/webhook-endpoints/{id}

Removes an endpoint; deliveries stop immediately.

Scope webhooks

Parameters

idrequireduuid · pathThe endpoint id.

Responses

200Deleted.
404No such endpoint.
curl -X DELETE https://api.verinode.ai/v1/webhook-endpoints/<id> \
  -H "Authorization: Bearer $KEY"

#GET/v1/outbound-events

Can't run a listener? Poll the recent outbound events for your operator.

Scope webhooks

Parameters

limitinteger · query1–100.
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/outbound-events"

Network (HQ)

Read the network layer with a group key (vn_group_live_…). Aggregates and compliance only, never a member's raw data. Read-only, 600/hour per endpoint. The keys:manage scope is selectable at mint time but required by no endpoint: it is reserved and grants nothing today. See the Network API (HQ) overview.

#GET/v1/network/ping

Authenticates the group key and echoes the network it resolves to. The fastest way to confirm HQ setup.

Responses

200{ ok, group_id, tier, scopes }.
401Missing or malformed key.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/network/ping

#GET/v1/network/summary

The group's latest network snapshot: membership counts, compliance rate, reputation and action-rate composites, margin percentiles. Sensitive metrics are suppressed (null) until a cohort is large enough to protect any single member; a k_anon block reports which thresholds were met.

Scope network:read

Responses

200{ data: NetworkSummary | null }.
403Missing the network:read scope.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/network/summary

#GET/v1/network/summary/history

The network-health snapshot series, most recent first, each row k-anon-gated identically to the latest snapshot. The time-series for your own warehouse. Cursor is a snapshot_date.

Scope network:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · querysnapshot_date to page before.

Responses

200{ data: NetworkSummary[], next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/summary/history?limit=90"

#GET/v1/network/members

The group's own member roster, cursor-paginated: location, owner, city, state, membership status, open and invite dates. Never a member's raw business data, contact details, or operator linkage.

Scope members:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/members?limit=50"

#GET/v1/network/compliance

Each member's certification and compliance posture, by name: cert type, status, expiration, carrier-program eligibility. Cursor-paginated. Roster and status only.

Scope compliance:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/compliance?limit=50"

#GET/v1/network/signals

The network pattern layer: how many members hit each signal (e.g. 12 of 30 on vendor cost drift), by severity and domain. Counts only, never member attribution. Every token ships a humanized _label. Cursor-paginated.

Scope network:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/signals?limit=50"

#GET/v1/network/programs

Adoption rates across the network by program (carrier / TPA / vendor): active vs adopted members and adoption percentage. Rates only, never which member declined. Cursor-paginated.

Scope network:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/programs?limit=50"

#GET/v1/network/process

Network-wide process-mining medians (jobs, recruiting, safety, supplements): stage-to-stage median / p25 / p75 days and sample size. The cross-network industry benchmark columns are stripped (the moat). Cursor-paginated.

Scope network:read

Parameters

limitinteger · query1–200 (default 50).
cursorstring · queryPage cursor.

Responses

200{ data, next_cursor, has_more }.
curl -H "Authorization: Bearer $KEY" "https://api.verinode.ai/v1/network/process?limit=50"

#GET/v1/network/export

Assembles the snapshot, full roster, and full compliance posture into one JSON bundle, using the same serialization as the individual endpoints. Synchronous (network data is bounded).

Scope exports:read

Responses

200{ data: { manifest, resources: { summary, members, compliance } } }.
curl -H "Authorization: Bearer $KEY" https://api.verinode.ai/v1/network/export