DocuClipper logo

Invoice OCR API

Not a developer? If you want invoice OCR software with a UI (no code), see Invoice OCR for accuracy benchmarks and screenshots, or Invoice Automation Software for the full AP workflow. This page is the developer REST API reference.

Upload a PDF invoice, receive a webhook with structured JSON: vendor, customer, invoice/PO numbers, dates, totals, taxes, and line items.

  • PAT auth on the /api/v1/agent/* endpoints
  • Webhook-driven (no polling)
  • Multi-page invoices, scanned PDFs, multi-line items
  • Extra fields (addresses, phone, payment terms) returned in extraFields

Prerequisites

  1. A Personal Access Token from Account → API. Set it as PAT=dcp_….
  2. An HTTPS endpoint to receive webhooks. The examples use webhook.site as a throwaway receiver for testing.

End-to-end example

bash
#!/usr/bin/env bash
set -euo pipefail
PAT="${PAT:?Set PAT to your dcp_… token}"
BASE="https://www.docuclipper.com"
PDF="invoice.pdf"

# 1. Throwaway public receiver for the demo. In production, point at your own HTTPS endpoint.
TOK=$(curl -s -X POST https://webhook.site/token -d '{}' -H 'Content-Type: application/json' | jq -r .uuid)
RECEIVER="https://webhook.site/$TOK"

cleanup() {
  [ -n "${WEBHOOK_ID:-}" ] && curl -s -X DELETE -H "Authorization: Bearer $PAT" "$BASE/api/v1/agent/webhooks/$WEBHOOK_ID" >/dev/null
  curl -s -X DELETE "https://webhook.site/token/$TOK" >/dev/null
}
trap cleanup EXIT

# 2. Register webhook
WEBHOOK_ID=$(curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
  -d "{\"url\":\"$RECEIVER\",\"events\":[\"invoice.extraction.completed\",\"document.extraction.failed\"]}" \
  "$BASE/api/v1/agent/webhooks" | jq -r .id)

# 3. Get presigned upload URL + PUT the file to S3
PRESIGN=$(curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
  -d "{\"filename\":\"$PDF\",\"mimetype\":\"application/pdf\"}" \
  "$BASE/api/v1/agent/documents/upload-url")
S3_URL=$(echo "$PRESIGN" | jq -r .url)
DOC_ID=$(echo "$PRESIGN" | jq -r .document.id)
curl -s -o /dev/null -X PUT -H "Content-Type: application/pdf" --data-binary "@$PDF" "$S3_URL"

# 4. Create job
JOB_ID=$(curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
  -d "{\"documents\":[$DOC_ID],\"jobType\":\"Invoice\"}" \
  "$BASE/api/v1/agent/jobs" | jq -r .jobId)
echo "job $JOB_ID — waiting for invoice.extraction.completed…"

# 5. Wait for the webhook. NOTE: this bash flow skips HMAC verification —
#    see the Python or Node.js tab for production code.
DEADLINE=$(( $(date +%s) + 600 ))
while [ $(date +%s) -lt $DEADLINE ]; do
  PAYLOAD=$(curl -s "https://webhook.site/token/$TOK/requests" | jq -r --arg jid "$JOB_ID" --arg ev "invoice.extraction.completed" '
    .data[]? | select(.headers["x-docuclipper-event"][0]==$ev) | select(.content|fromjson|.job.id==$jid) | .content' | head -1)
  if [ -n "$PAYLOAD" ]; then echo "$PAYLOAD" | jq .data; exit 0; fi
  sleep 2
done
echo "timed out" >&2; exit 1

Webhook payload

Real data field from a successful run on a real invoice.

json
{
  "2666984": {
    "invoiceMode": [
      {
        "invoiceId": "101",
        "date": "2016-08-01",
        "dueDate": "2016-08-31",
        "vendor": "",
        "customer": "",
        "poNumber": "",
        "total": "129",
        "subTotal": "120",
        "tax": "9",
        "lines": [
          {
            "item": "Mowing service for July, 2016. Includes turf mowing, edging, trimming, blowing off surface areas. Pick up and removal of small trash/debris.",
            "quantity": "1",
            "unitPrice": "120",
            "tax": "",
            "category": "Uncategorized",
            "service": "",
            "extraFields": {}
          }
        ],
        "extraFields": {
          "NAME": "Kate L",
          "ADDRESS": "Kate L\n2317 Broadway, Redwood City, CA\n94063",
          "NAME2": "Affordable Lawn Care",
          "ADDRESS2": "PO Box 441, Burlington, KY. 41005",
          "VENDOR_PHONE": "859-802-2987",
          "VENDOR_URL": "https://invoice.2go.com",
          "AMOUNT_DUE": "$129.00",
          "AMOUNT_PAID": "$0.00",
          "PAYMENT_TERMS": "NET 30"
        }
      }
    ]
  }
}

Field reference

FieldTypeDescription
[documentId].invoiceMode[]arrayOne entry per invoice on the document
invoiceMode[].invoiceIdstringInvoice number / ID
invoiceMode[].datestringInvoice date (YYYY-MM-DD)
invoiceMode[].dueDatestringDue date (YYYY-MM-DD)
invoiceMode[].vendorstringVendor / supplier name
invoiceMode[].customerstringCustomer / bill-to name
invoiceMode[].totalstringTotal invoice amount
invoiceMode[].subTotalstringSub-total (before tax)
invoiceMode[].taxstringTax amount
invoiceMode[].poNumberstringPurchase order number
invoiceMode[].lines[]arrayLine items: item, quantity, unitPrice, tax, category
invoiceMode[].extraFieldsobjectAdditional vendor/customer fields detected (addresses, phone, payment terms…)

Notes & gotchas

  • All numeric fields are returned as strings — invoice totals are returned as "129", not 129. Cast on your side when arithmetic is needed.
  • Multi-invoice PDFs return multiple entries in invoiceMode[], one per detected invoice on the document.
  • extraFields is a free-form bag of additional fields the extractor recognized. Schema is invoice-specific.