Async processing
Every extraction job runs asynchronously. The recommended path is to register a webhook and let DocuClipper push the completed extraction to you. Polling GET /api/v1/agent/jobs/:id is supported as a fallback.
Webhooks (recommended)
Subscribe to the doctype-specific event for your job:
bank_statement.extraction.completed— bank statements + check imagesinvoice.extraction.completed— invoices / receiptsform.extraction.completed— tax formsdocument.extraction.failed— always subscribe to catch errors
Full setup, signature verification, retries, and cleanup live on the Webhooks page.
Polling (fallback)
bash
curl -s -H "Authorization: Bearer $PAT" \
"https://www.docuclipper.com/api/v1/agent/jobs/12345"Sample response when the job is finished:
json
{
"id": "12345",
"name": "sdk-demo-bank",
"status": "Succeeded",
"type": "ExtractData",
"isGeneric": false,
"isBankMode": true,
"reviewed": false,
"createdAt": "2026-05-26T15:27:04.682Z",
"updatedAt": "2026-05-26T15:27:25.911Z",
"documents": [
{ "id": "2666907", "originalname": "statement.pdf", "mimetype": "application/pdf", "numPages": 4, "isProcessed": true, "isScanned": false }
],
"summary": { "totalDocuments": 1, "totalPages": 4, "transactionLineCount": 137 }
}Job statuses
Pending— queued, not yet picked up by a workerInProgress— worker actively processingSucceeded— done; webhook fired (if subscribed) andGET /agent/jobs/:id/datais readyPartiallySucceeded— a long file converted in parts: every part is done and some failed (see Long files)Failed— seedocument.extraction.failedwebhook for the error message
Long files
A PDF over 250 pages, or an upload with more pages than one job takes, is converted in parts automatically, as one or more jobs. Pages cost the same. Keep the jobId that POST /agent/jobs returned (its response says "convertsInParts": true):
GET /agent/jobs/:idreports the status of the whole upload,Succeededonly once every part is, and lists the parts underupload.parts./transactionsand/datafor that id return every part's rows in page order.- The document id you uploaded answers for its parts:
GET /agent/documents/:documentIdreturns"convertedInParts": trueand the parts with their page ranges. - Webhooks fire once for the whole upload, under the
jobIdyou created, when every part is done. The payload covers every part that succeeded, andjob.uploadlists each part with its status. A failed part converted again later sends another event for the samejobId: each event is a full snapshot, so replace rather than append. - If some parts fail, the status is
PartiallySucceeded: the data reads and the webhook carry the parts that succeeded, andupload.partssays which failed. Open a failed part in DocuClipper and click Convert again to retry it.
Why webhooks beat polling
- No wasted requests. Most polls during the average job's ~30 second runtime return
InProgress. Skip them. - The payload is free. The webhook body already contains the full extracted JSON — you don't need a follow-up
GET /agent/jobs/:id/datacall to get the result. - Failure handling.
document.extraction.failedcarries the error message inline. Polling makes you reconstruct it from the job row.