Connect DocuClipper to AI Agents (MCP)
Give an AI agent access to DocuClipper extraction through the MCP endpoint, with a Personal Access Token or OAuth sign-in. Discover the tool registry, call tools over HTTP, and convert bank statements or invoices without a manual upload step.
Last updated
DocuClipper exposes its extraction tools to AI agents through a set of Model Context Protocol (MCP) style tool endpoints. An agent can discover the available tools, call them, and get structured transactions back - no manual uploading.
Using ChatGPT or Claude? Add
https://www.docuclipper.com/api/v1/agent/mcpas a custom MCP server or connector and sign in to DocuClipper when asked. No token needed: see Connect DocuClipper to ChatGPT or Claude. This article covers agents and scripts you run yourself.
Two transports, one tool registry. If you have an MCP client, point it at
POST https://www.docuclipper.com/api/v1/agent/mcp, which speaks JSON-RPC 2.0 / Streamable HTTP (initialize,tools/list,tools/call,ping). It is stateless, so there is no session id to store. If your runtime just calls HTTP tools, use the plain JSON-over-HTTP routes under/api/v1/agent/mcp/toolsdocumented below. Tool names and input schemas are identical either way. There is no standalonenpxpackage to drop into Claude Desktop or Cursor yet.
Prerequisites
- A DocuClipper account with a Personal Access Token. Get yours at Settings → API & Webhooks (
/account?section=api). See API Access: Personal Access Tokens. - A client that can send authenticated HTTP requests (an agent framework, a script, or your own code).
All requests use the base URL https://www.docuclipper.com/api/v1/agent/mcp and the header Authorization: Bearer <PAT> (PATs start with dcp_).
The endpoint is also an OAuth 2.1 protected resource, for MCP clients that sign the user in instead of taking a token. A request without credentials gets 401 with WWW-Authenticate: Bearer resource_metadata="https://www.docuclipper.com/.well-known/oauth-protected-resource/api/v1/agent/mcp", and the authorization server metadata is at https://www.docuclipper.com/.well-known/oauth-authorization-server. Clients register themselves (dynamic client registration), use the authorization code flow with PKCE (S256), and get a one-hour access token plus a rotating refresh token, scope mcp. An OAuth access token reaches the same tools as a PAT.

Discover the tools
The tool registry is discoverable at runtime, so your agent always gets the current set:
TOKEN="dcp_xxxxxxxx"
BASE="https://www.docuclipper.com/api/v1/agent/mcp"
# List all tools (name, description, JSON input schema).
curl -sS "$BASE/tools" -H "Authorization: Bearer $TOKEN"
# Get the schema for one tool.
curl -sS "$BASE/tools/convert_bank_statement/schema" -H "Authorization: Bearer $TOKEN"
Call a tool
POST to /tools/<name> with the tool's parameters as a JSON body:
# One-shot: upload a base64 PDF, extract, poll, and return transactions.
curl -sS -X POST "$BASE/tools/convert_bank_statement" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"filename\":\"statement.pdf\",\"mimetype\":\"application/pdf\",\"fileBase64\":\"$(base64 -i statement.pdf)\",\"format\":\"csv\"}"
The response is { "result": ... }, where result is the tool's output.
Available tools
| Tool | What it does |
|---|---|
convert_bank_statement | One-shot: takes a file (a host attachment as file, or base64 bytes as fileBase64 with filename and mimetype), uploads it, extracts, polls until done, and returns transactions (JSON or CSV) with the reconciliation result. file takes up to 100 MB, fileBase64 up to about 18 MB. |
upload_url | Gets a presigned upload URL for sending a large file straight to storage. Returns the url, the method (PUT) and headers to send with it, and the document id. |
convert_document | Enqueues an extraction job for one or more documents you already uploaded and returns the job id at once. documentType is bank_statement (default), invoice or receipt. |
convert_invoice | One-shot for an invoice, bill or receipt: takes the file the same way as convert_bank_statement and returns each invoice's header fields, line items and consistency check. |
get_invoices | Reads the invoices or receipts of a document (documentId) or job (jobId): vendor, customer, invoice number, dates, PO number, currency, subtotal, tax, total, line items, and the consistency check. Pages with limit, offset and, for an invoice with very many line items, lineItemOffset. |
get_job_status | Polls a job by id and returns its status, transaction count and, once it has succeeded, how many statement periods reconciled. Status is InProgress (or Finishing) until the job ends as Succeeded, PartiallySucceeded, Failed or Canceled; OutOfCredits means it is waiting for pages. While the uploaded file is still being checked, the result has "stage": "validating" in place of the page and document counts. |
download_transactions | Fetches transactions from any completed job by id, as JSON or CSV, with the reconciliation result. Pages with limit (max 10,000) and offset. |
get_transactions | Alias of download_transactions. |
list_projects | Lists the projects on your account the token can read. Start here to reach documents you did not upload through the API. |
list_documents | Lists the documents in a project, newest first, with extraction status, page count, transaction count, and whether the statement reconciled. |
list_recent_documents | Lists the documents you uploaded most recently, newest first; since (an ISO 8601 time) limits it to uploads after that moment. |
get_document | Status and summary for one document, including the project it belongs to. |
get_document_transactions | Fetches the extracted rows for one document, as JSON or CSV, with the reconciliation result. Pages with limit and offset. |
get_fraud_report | Checks a converted bank or credit card statement, invoice or receipt, or pay stub or tax form (documentId) for signs of tampering: the 0-100 authenticity score, its risk band, and each signal with its confidence and a plain-language description. See Fraud reports below. |
get_upload_link | Returns the DocuClipper upload screen where a person uploads a statement (or, with documentType: "invoice" or "receipt", an invoice or receipt) themselves, opened in the project they last converted into (project in the result), plus a since time to pass to list_recent_documents afterwards. For clients that cannot pass files to a tool. |
Every tool declares MCP annotations: a title, and readOnlyHint, destructiveHint and openWorldHint. The read tools are read-only; convert_bank_statement, convert_document and upload_url create documents and use pages, and are marked as writes. No tool deletes or overwrites anything.
Large files
For a file bigger than fileBase64 takes (about 18 MB), or any file your agent has on disk, upload it through the presigned URL instead of convert_bank_statement:
- Call
upload_urlwithfilenameandmimetype. PUTthe raw file bytes to the returnedurlwith the returnedheaders(Content-Typeset to the mimetype), withinexpiresInseconds (one hour):curl -sS -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @statement.pdf- Call
convert_documentwithdocumentIds: [document.id]. It returns ajobIdat once. - Poll
get_job_statusevery 30 to 60 seconds until the status isSucceededorPartiallySucceeded. A 250-page statement takes about 6 minutes. - Read the rows with
download_transactions(byjobId) orget_document_transactions(bydocument.id).
A PDF over 250 pages is converted in parts automatically, under the same jobId (see Long files below).
Invoices and receipts
convert_invoice and get_invoices return one object per invoice:
{
"documentId": 8461240, "pageNumber": 1, "docType": "bill",
"vendor": "Northwind Industrial Supply", "customer": "Smith & Co Fabrication",
"invoiceNumber": "NW-55821", "invoiceDate": "2026-09-30", "dueDate": "2026-10-30",
"poNumber": null, "currency": "USD", "subtotal": 10155.9, "tax": 863.25, "total": 11019.15,
"consistency": { "isConsistent": true, "totalsMatch": true, "lineItemsMatchSubtotal": true,
"totalsDelta": 0, "lineItemsDelta": 0, "unverifiable": false, "notAnInvoice": null },
"lineItemCount": 150,
"lineItems": [ { "description": "Part 001 - Hex bolt M8 zinc", "productCode": null,
"quantity": 1, "unitPrice": 3.15, "amount": 3.15, "tax": null } ]
}
consistency is the same verdict the invoice editor's badge shows: totalsMatch is subtotal + tax (+ printed charges) = total, lineItemsMatchSubtotal is the line items adding up to the subtotal, and notAnInvoice names a document that is not a bill (a statement of account, a ledger, a quote), which is labelled rather than judged. The result also has a summary with how many invoices are consistent. download_transactions and get_document_transactions answer an invoice document with a pointer to get_invoices, and get_invoices points a bank statement back to them.
Fraud reports
get_fraud_report returns the same analysis as the Fraud Detection tab in the web app (see Understanding Bank Statement Fraud Detection and Invoice and Pay Stub Fraud Detection):
{
"documentId": 2866725, "name": "march-statement.pdf", "documentType": "bank_or_credit_card_statement",
"available": true, "analyzed": true,
"score": 98, "band": "authentic", "label": "Appears authentic (90-100)",
"interpretation": "No meaningful signs of editing were found in this statement.",
"pdfChecksApplied": true,
"checksRun": "PDF structure and metadata checks, bank layout fingerprint, consistency with the same account's other statements in the project plus balance reconciliation",
"signalCount": 1,
"signals": [
{ "type": "DOESNT_MATCH_KNOWN_FINGERPRINTS", "label": "Layout not matched to a known bank template",
"description": "The document's fonts and structure do not match a layout already confirmed as a genuine bank statement. ...",
"confidence": 0.5, "details": "...", "pageNumber": null, "period": null, "text": null }
],
"caveat": "The authenticity score is a risk signal to guide review, not proof that a document is genuine or fake."
}
bandisauthentic(90-100),minor_concerns(70-89),moderate_risk(50-69) orhigh_risk(0-49).documentTypeisbank_or_credit_card_statement,invoice_or_receipt,pay_stubortax_form. Invoices also get the invoice math check (INVOICE_MATH_MISMATCH), pay stubs the pay stub checks (PAYSTUB_MATH_MISMATCH,PAYSTUB_YTD_INCONSISTENT,PAYSTUB_WITHHOLDING_ANOMALY,PAYSTUB_CURRENT_VS_YTD_MISMATCH, andPAYSTUB_NET_PAY_NOT_DEPOSITEDwhen the project holds the bank statements for the pay date), and tax forms the PDF checks only. Statements are also compared with the other documents of their project (PRODUCER_DIFFERS_FROM_SIBLINGS,BALANCE_CONTINUITY_BREAK,RECONCILIATION_FAILED_UNLIKE_SIBLINGS) and with their own printed totals (PRINTED_TOTALS_CONTRADICT); every signal comes with a plain-languagelabelanddescription.checksRunsays which checks ran, andinterpretationnames who to verify the document with (the bank, the vendor or the employer).- A document uploaded as an image, photo or CSV gets the math checks only, and the result says
"pdfChecksApplied": false. - A file converted in parts is scored part by part under
parts; the top-level score is the lowest-scoring part. - A document still being converted returns
"analyzed": falsewithstatus; ask again onceget_documentreportscompleted. - The first report for a document is computed when you ask for it: the call waits up to about 40 seconds, and if the score is not ready by then it returns
"scoring": truewithstatus: "scoring". Callget_fraud_reportagain a little later. A report whose score is being refreshed comes back at once with"rescoring": true. - Fraud detection is included on Business and Enterprise plans and during the free trial. On other plans the call succeeds with
"available": false, amessage, andupgradeUrl(DocuClipper's plans page).
Card and account numbers
Everything the agent API and the MCP tools return shows card and bank account numbers as their last 4 digits (****4821): in description, in account, in rawCells and in each statement period's account. Amounts, dates, check numbers and reference or trace ids are returned as printed. The web app and its downloads keep the full numbers.
Reconciliation and paging in results
The tools that return transactions (convert_bank_statement, download_transactions, get_document_transactions) include a reconciliation object: one entry per statement period with its account, dates, printed opening and closing balances and isReconciled (whether the extracted rows tie the opening balance to the closing balance), plus counts:
"reconciliation": {
"periods": 3, "reconciled": 2, "unreconciled": 1, "unchecked": 0, "allReconciled": false,
"statementPeriods": [
{ "documentId": 8461233, "account": "****4821", "startDate": "2026-01-01", "endDate": "2026-01-31",
"startBalance": 10234.17, "endBalance": 9876.02, "isReconciled": true }
]
}
Results also carry total, offset, hasMore and nextOffset. Over the MCP JSON-RPC endpoint, each result is kept under the size AI hosts accept (Claude caps a tool result at about 150,000 characters, Claude Code at 25,000 tokens), so a long statement comes back over several calls: when hasMore is true, call again with offset set to nextOffset. There, each row's original table cells (rawCells) are left out unless you pass includeRawCells: true. The plain HTTP routes return up to limit rows (default 1,000) with rawCells, as before.
Passing files from a chat
- ChatGPT attaches files to tool calls itself:
convert_bank_statementdeclares itsfileargument in_meta["openai/fileParams"], ChatGPT passes{ download_url, file_id, mime_type, file_name }, and DocuClipper downloads the file over https. - Claude (claude.ai and the Claude desktop and mobile apps) does not pass chat attachments to connectors, and its code sandbox cannot upload to DocuClipper's storage. DocuClipper recognizes Claude by the app the connection was made with and shows it a tool list and instructions built for that: the assistant calls
get_upload_linkas soon as a file is attached, the user uploads it in DocuClipper, and the assistant finds it withlist_recent_documentsusing thesincevalue it got.upload_urlandconvert_documentare not listed there, and the convert tools take nofileargument. - Agents and scripts you run yourself (Claude Code, Cursor, your own code) hold files on disk: send them as
fileBase64or throughupload_url.
list_recent_documents lists only files that reached DocuClipper: a document created by upload_url whose upload never arrived is left out.
Reading documents you did not upload
download_transactions and get_job_status take a job id, and a job id only exists if this token created the job. Documents that arrive another way, through a connected Box, Dropbox, Google Drive, or OneDrive folder, an ingestion email address, or a colleague working in the web UI, have no job id your agent can guess.
Find those by project instead:
# 1. Which projects can this token see?
curl -sS -X POST "$BASE/tools/list_projects" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'
# 2. What is in one of them? (newest first)
curl -sS -X POST "$BASE/tools/list_documents" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"projectId":319091,"limit":50}'
# 3. Read the rows for a document from that list.
curl -sS -X POST "$BASE/tools/get_document_transactions" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"documentId":8461233,"format":"csv"}'
Each document in step 2 comes back with a status (pending, processing, completed, failed, canceled, or out_of_credits), a source telling you how it arrived (box_folder, dropbox_folder, gdrive_folder, onedrive_folder, digital, or camera), its transactionCount, and isReconciled for bank statements. Large projects page with limit (max 200) and offset.
Free trial accounts
A trial account exports a sample: up to 10 transactions (3 invoices or receipts, 3 forms) per account across all jobs and every export path, the API included. When a result is a sample, the tools that return rows (convert_bank_statement, download_transactions, get_document_transactions) add a trial object:
"trial": {
"limited": true,
"returned": 10,
"total": 342,
"upgradeUrl": "https://www.docuclipper.com/pricing/",
"message": "Free trial: showing 10 of 342 transactions. ... Paid plans include every row: https://www.docuclipper.com/pricing/"
}
Show trial.message to your user so they know the rows are a sample. upgradeUrl is DocuClipper's plans page (the field keeps its name for existing clients). Once the allowance is used, a read of new rows returns that next step instead (MCP: an isError result with relayToUser: true, message and upgradeUrl; plain HTTP: 402). The REST endpoints add trial to JSON bodies that list transactions, and send X-DocuClipper-Trial-Sample: <returned>/<total> and X-DocuClipper-Upgrade-Url headers on every sampled response, CSV included.
Timeouts
convert_bank_statement blocks while it polls, and the wait is capped at 50 seconds by the CDN edge timeout (default 45). If the job is still running when the wait expires you get {"timedOut": true, "jobId": ...} back rather than an error, so keep the job id and finish with get_job_status and get_document_transactions. For anything beyond roughly a hundred pages, pass "async": true to skip the wait entirely, or use the async path above.
Long files: a PDF over 250 pages is converted in parts automatically, and pages cost the same. The jobId a tool gives you covers every part: get_job_status says Succeeded only when all parts are and lists them under upload, and download_transactions returns every part's rows in page order, with rowNumber running across the whole file (each row's documentId and pageNumber name the part and its page within that part). The documentId you uploaded does the same in get_document and get_document_transactions. If some parts fail, the status is PartiallySucceeded: you get the rows of the parts that succeeded, and upload.parts names the failed ones, which you can open in DocuClipper and convert again (Convert again at the top of its results page). A file this long takes a few minutes, so pass "async": true to convert_bank_statement, or upload it as in Large files above.
Troubleshooting
HTTP 401: the PAT is missing, invalid, expired, or was revoked. Send it as Authorization: Bearer dcp_... and generate a new one at Settings → API & Webhooks if needed. For an app connected by signing in, the access token expired (refresh it) or the app was disconnected under Settings → API & Webhooks → Connected apps (connect it again).
HTTP 402 / out of pages: your account is out of pages. Top up at Settings → Plan & Billing. A job already submitted that ran out of pages is held and does not restart on its own: after topping up, open it in DocuClipper and click Resume processing, or submit the documents again. While a job is held, get_job_status returns "heldForPages": true with a message saying what to do, convert_bank_statement stops waiting and returns the same, and the transaction tools answer that the job is waiting for pages instead of returning an empty list.
Subscription payment due: if your subscription payment didn't go through, documents you submit are saved and held the same way (out_of_credits), and the message says the job is waiting for a payment. Update your payment method in Settings → Plan & Billing; held documents are processed automatically once the payment goes through. See When a Subscription Payment Fails.
404 "Tool not found": the tool name is misspelled or not in the registry. Call GET /tools to see the exact names.
A tool call returns 500: the underlying extraction failed. Check the job with get_job_status, and see API: common recipes for the equivalent raw endpoints and error shapes.
Related
- Connect DocuClipper to ChatGPT or Claude
- MCP integration overview - supported clients and use cases
- API Access: Personal Access Tokens
- API: common recipes
- Webhooks overview
- API reference