DocAI API Documentation

DocAI provides a REST API for extracting structured data from uploaded documents or normalized inbound-email attachments. Persistent jobs, human approval, signed webhooks, and accounting exports support automation workflows in Make, n8n, Zapier, or custom integrations.

Building from a coding agent instead of application code? DocAI MCP connects Codex, Claude Code, Gemini CLI and other MCP-compatible agents to this same API.

Authentication

Create and manage API keys in the Developer Settings page. Send the key as a bearer token; scopes and the key's monthly page quota are enforced on every request.

Authorization: Bearer sk_docai_<your_key>

API keys start with sk_docai_. Keep them secret. They are shown only once at creation.

Use the extract scope to submit and monitor jobs. Add the history scope to fetch saved results and accounting exports. These are the only supported API-key scopes.

Rate Limits

Analysis usage is governed by your plan's page allowance and storage quota. There is no separate per-request or per-day cap.

API keys additionally meter OCR pages against the key's monthly page quota.

Requests that run out of pages or storage return HTTP 402; an API key over its monthly page quota returns HTTP 429 with a Retry-After hint.

Error Format

{
  "detail": "File too large. Maximum upload size is 15 MB.",
  "request_id": "abc123"
}

Sync Document Extraction

POST/api/v1/analyze

Upload a document and get extraction results synchronously. Best for small documents (< 2 pages).

Request

Multipart form data:

FieldTypeDescription
filefilerequiredPDF, PNG, JPG, HEIC, AVIF, TIFF, BMP, or WEBP (max 15MB)
filenamestringoptionalOverride the displayed filename

Example

curl -X POST https://docai.synairo.com/api/v1/analyze \
  -H "Authorization: Bearer sk_docai_..." \
  -F "file=@purchase_order.pdf"

Response

{
  "request_id": "3fa8...",
  "filename": "purchase_order.pdf",
  "document_type": "purchase_order",
  "document_type_confidence": 0.94,
  "summary": "Purchase order from Acme Corp to Widget Co.",
  "language": "en",
  "extracted_fields": [
    {
      "field": "po_number",
      "value": "PO-2026-0042",
      "confidence": 0.97,
      "page": 1,
      "evidence": "PO-2026-0042"
    }
  ],
  "warnings": [],
  "ocr": { "pages": 1, "tokens": 312, "language": "eng+pol" },
  "llm": { "status": "success", "provider": "openai", "model": "gpt-4o-mini" }
}

Streaming Extraction (SSE)

POST/api/v1/analyze/stream

Same as sync, but streams Server-Sent Events showing processing progress. Best for real-time UIs.

Create Async Job

POST/api/v1/jobs

Upload a document and get a job ID. Process happens in the background.

curl -X POST https://docai.synairo.com/api/v1/jobs \
  -H "Authorization: Bearer sk_docai_..." \
  -F "file=@invoice.pdf"
{ "job_id": "abc123", "status": "queued", "request_id": "xyz..." }

Ingest Email Attachments

POST/api/v1/ingest/email

Provider-neutral inbound-email bridge. Postmark, Mailgun, SendGrid, Amazon SES, Make, n8n, or another adapter must parse the provider payload and send accepted attachments as multipart form data. DocAI does not host a mailbox or perform provider-specific MIME parsing.

FieldTypeDescription
message_idstringrequiredStable message identifier from the provider
source_namespacestringrequiredStable provider/account scope, such as postmark:server-123
filesfile[]requiredRepeat for every supported attachment
organization_idstringoptionalTarget workspace; the caller must have upload permission
curl -X POST https://docai.synairo.com/api/v1/ingest/email \
  -H "Authorization: Bearer sk_docai_..." \
  -F "message_id=provider-message-42" \
  -F "source_namespace=postmark:server-123" \
  -F "organization_id=org_..." \
  -F "files=@invoice.pdf;type=application/pdf" \
  -F "files=@receipt.jpg;type=image/jpeg"
{
  "message_id": "provider-message-42",
  "source_namespace": "postmark:server-123",
  "accepted_count": 1,
  "duplicate_count": 1,
  "jobs": [
    {"job_id": "job-new", "status": "queued", "filename": "invoice.pdf", "duplicate": false},
    {"job_id": "job-old", "status": "completed", "filename": "receipt.jpg", "duplicate": true}
  ]
}

Idempotency includes the authenticated owner, provider/account namespace, destination, message ID, normalized filename, file content, and occurrence among identical attachments. Reordered provider retries return the original job IDs. A failed ingestion can requeue the same job with fresh bytes up to three total attempts; active and completed jobs are never processed twice.

Get Job Status

GET/api/v1/jobs/{job_id}

curl https://docai.synairo.com/api/v1/jobs/abc123 \
  -H "Authorization: Bearer sk_docai_..."
{
  "id": "abc123",
  "status": "completed",
  "progress_pct": 100,
  "document_type": "invoice",
  "pages_processed": 2
}

Possible statuses: queued, processing, review_required, completed, failed, cancelled.

Get Job Result

GET/api/v1/jobs/{job_id}/result

Returns the full extraction result once job status is completed or review_required. Requires the history scope for API keys.

curl https://docai.synairo.com/api/v1/jobs/abc123/result \
  -H "Authorization: Bearer sk_docai_..."

Export JSON

POST/api/v1/export/json

Send extraction result as JSON body, get clean export file.

curl -X POST https://docai.synairo.com/api/v1/export/json \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_docai_..." \
  -d '{"request_id":"...","extracted_fields":[...]}' \
  -o export.json

Export CSV

POST/api/v1/export/csv

curl -X POST https://docai.synairo.com/api/v1/export/csv \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_docai_..." \
  -d '{"request_id":"...","extracted_fields":[...]}' \
  -o fields.csv

Export XLSX

POST/api/v1/export/xlsx

Returns an Excel workbook with Summary, Fields, Line Items, Warnings, and Metadata sheets.

List History

GET/api/v1/history

curl https://docai.synairo.com/api/v1/history \
  -H "Authorization: Bearer sk_docai_..."

Review and Approval Workflow

POST/api/v1/history/{record_id}/workflow

Apply a controlled workflow transition to a saved record. This route requires an interactive Clerk session: API keys cannot approve documents. Workspace members can review; workspace admins can approve, request changes, or reject. A personal-record owner performs both roles.

{
  "action": "approve",
  "comment": "Ready to post"
}

Actions: start_review, complete_review, reopen_review, submit_for_approval, approve, request_changes, reject, and accept_partial_analysis. Approval commits the workflow audit event and document.approved outbox event together.

Delete All History

DELETE/api/v1/history

Permanently deletes all saved analyses for your account.

List API Keys

GET/api/v1/keys

Create API Key

POST/api/v1/keys

curl -X POST https://docai.synairo.com/api/v1/keys \
  -H "Authorization: Bearer <clerk_session_token>" \
  -H "Content-Type: application/json" \
          -d '{"name": "My automation key", "scopes": "extract,history"}'
{
  "id": "...",
  "name": "My automation key",
  "key": "sk_docai_...",   // shown ONCE - save it now
  "prefix": "sk_docai_abc",
  "scopes": "extract,history",
  "created_at": "2026-06-10T12:00:00Z"
}

Revoke API Key

DELETE/api/v1/keys/{key_id}

Create Webhook

POST/api/v1/webhooks

curl -X POST https://docai.synairo.com/api/v1/webhooks \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
          -d '{"name": "My webhook", "endpoint_url": "https://my.app/hook", "event_types": ["document.completed", "document.review_required", "document.approved", "document.failed"]}'

The response includes a secret for signature verification. Save it now, it won't be shown again.

Webhook Event Types

EventWhen it fires
document.completedExtraction completed and the result was saved
document.review_requiredValidation or confidence routing requires human review; also fires with document.completed
document.approvedAn authorized approver committed the approval decision; use this for accounting writes
document.failedProcessing failed; email-ingested jobs include retryability and attempt metadata
Use document.approved, not document.completed, as the control point for posting data to an accounting system. Extraction can complete while fields still require correction.

Test Webhook

POST/api/v1/webhooks/{webhook_id}/test

Verify Webhook Signature

Every delivery includes these headers:

import hashlib, hmac

def verify(secret, body_str, timestamp, signature_header):
    msg = f"{timestamp}.{body_str}"
    digest = hmac.new(secret.encode(), msg.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={digest}", signature_header)

Failed Durable Events

DocAI retries outbound deliveries automatically. If a durable outbox event cannot be expanded after five attempts, it becomes a dead letter. The owner can inspect metadata without exposing the stored payload and replay the event after correcting the cause.

GET/api/v1/webhook-outbox/failed

curl https://docai.synairo.com/api/v1/webhook-outbox/failed \
  -H "Authorization: Bearer sk_docai_..."

POST/api/v1/webhook-outbox/{event_id}/replay

curl -X POST https://docai.synairo.com/api/v1/webhook-outbox/EVENT_ID/replay \
  -H "Authorization: Bearer sk_docai_..."

The same controls are available under Developer Settings β†’ Events needing attention. Delivery and terminal outbox records are retained for 30 days.

Use with Make / n8n / Zapier

For direct uploads, call /api/v1/jobs. For inbound email, let the provider parse attachments and call /api/v1/ingest/email with a stable message ID and source namespace. Both routes create the same persistent jobs and use the same review workflow.

Subscribe to completion and review events for status, then use document.approved to trigger controlled downstream accounting actions. Signed webhooks remove the need to poll.