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
- A Personal Access Token from Account → API. Set it as
PAT=dcp_…. - 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 1Webhook 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
| Field | Type | Description |
|---|---|---|
| [documentId].invoiceMode[] | array | One entry per invoice on the document |
| invoiceMode[].invoiceId | string | Invoice number / ID |
| invoiceMode[].date | string | Invoice date (YYYY-MM-DD) |
| invoiceMode[].dueDate | string | Due date (YYYY-MM-DD) |
| invoiceMode[].vendor | string | Vendor / supplier name |
| invoiceMode[].customer | string | Customer / bill-to name |
| invoiceMode[].total | string | Total invoice amount |
| invoiceMode[].subTotal | string | Sub-total (before tax) |
| invoiceMode[].tax | string | Tax amount |
| invoiceMode[].poNumber | string | Purchase order number |
| invoiceMode[].lines[] | array | Line items: item, quantity, unitPrice, tax, category |
| invoiceMode[].extraFields | object | Additional vendor/customer fields detected (addresses, phone, payment terms…) |
Notes & gotchas
- All numeric fields are returned as strings — invoice totals are returned as
"129", not129. Cast on your side when arithmetic is needed. - Multi-invoice PDFs return multiple entries in
invoiceMode[], one per detected invoice on the document. extraFieldsis a free-form bag of additional fields the extractor recognized. Schema is invoice-specific.