API and webhooks
Run the same deterministic LC check from your own system: the same extraction, the same UCP 600 and ISBP 821 rules, the same result as in the browser. API access is part of the Enterprise plan.
What you can do
A deliberately small, versioned surface under /api/v1. A partner check goes through exactly the same extraction, redaction and rule engine as a check in the browser.
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /api/v1/ping | any active key | Confirms your key works. Returns the account email and plan. Spends no credit. |
| GET | /api/v1/usage | any active key | Plan, included checks per period, checks used this period, remaining check credits and period end. Spends no credit. |
| POST | /api/v1/lc-checks | lc:write | Runs a full LC Clear check on the documents you upload and returns the result. Billed exactly like a check in the browser. |
| GET | /api/v1/lc-checks/{id} | lc:read | Fetches a past result again. Only checks belonging to the key's own account. |
| POST | /api/v1/lc-cases/{case_id}/document-checks | lc:write | ERP pre-check: checks the invoice (optionally with the bill of lading and the sales order) against the credit saved with one of your cases, without requiring the transport document. Billed like a check. |
| POST | /api/v1/lc-reviews | lc:write | Reviews the credit itself before any document exists: the LC Workability Review (side=exporter) or the LC Pre-Issuance Review (side=importer). Spends no check credit. |
| GET | /api/v1/lc-cases | lc:read | The cases of your account, each with whether a credit is saved to it: how you find the case_id. Spends no credit. |
| GET | /api/v1/finding-codes | any active key | Catalogue of every explanation_code an LC Clear check can return, with its params, possible severities, the date it was added and, for a retired code, the date it was retired, plus the current rule set version. Spends no credit. |
Authentication
The API authenticates with API keys, not a username and password, so your integration does not break when someone changes their password.
- You create keys yourself under Settings in your DocAccord account (verified email address required).
- Every request sends the key as a header:
Authorization: Bearer da_live_.... Browser session logins are not accepted on /api/v1. - The key is shown exactly once. Only a SHA-256 hash and a short prefix for recognition are stored. A lost key is replaced, not recovered.
- Scopes:
lc:readfetches results,lc:writeruns billable checks. New keys get both; you can narrow a key to one scope in Settings, for example for a system that only collects results. - Choose the lifetime: 30, 90, 180, 365 (default) or 730 days. Every new key expires. Up to 10 active keys per account. A revoked key stops working immediately. The owner and admins of the organisation see and revoke every key of the organisation.
- Rotating without a gap: create the new key and name the one it replaces (
replaces, optionaloverlap_hoursfrom 1 to 72, default 24). The old key then expires after that period instead of being revoked at once. - The Enterprise plan is checked on every call, not only at creation. If an account leaves the Enterprise plan, its keys answer with 403.
Your first call
Start with ping. It resolves your key, runs no check and costs nothing.
curl https://api.docaccord.com/api/v1/ping \
-H "Authorization: Bearer da_live_YOUR_KEY"{
"ok": true,
"account_email": "trade-ops@example.com",
"plan": "ENTERPRISE"
}If the key is missing, unknown, revoked or expired, the API answers with 401.
Run a check
One multipart/form-data request with your documents. The response comes back synchronously once the check is stored.
- Required: the credit and the commercial invoice. Each document either as a file (
lc_file,invoice_file) or as text (lc_text,invoice_text), never both. - Transport documents, same pattern (
_fileor_text):bill_of_lading,multimodal_transport_doc,air_transport_doc,road_rail_inland_waterway_doc,charter_party_bill_of_lading,sea_waybill,courier_receipt. At least one is required unless field 46A of the credit asks for none; that is checked once the credit is read, before anything is billed. - Further transport sets (for example a second bill of lading, or an air waybill next to the bill of lading): up to three files in
additional_transport_files, one set per file, with its type at the same position inadditional_transport_types(such asBILL_OF_LADING,AIR_TRANSPORT_DOC). A check stays one credit. - Optional, same pattern:
packing_list,weight_list,certificate_of_origin,insurance_certificate,bill_of_exchange,beneficiary_certificate,inspection_certificate. Plus the shipment advice as text inshipment_advice_text(a file variant follows once the size limit at the entry point is adjusted). - Files up to 20 MB: PDF, scans and photos (PNG, JPEG, GIF, WEBP, TIFF, BMP), DOCX, XLSX and text formats. The format is detected from the file content, not its name. The invoice may also be an EN 16931 e-invoice (XRechnung, ZUGFeRD, Factur-X, UBL or CII): as an XML file, or as a PDF with the XML attached. An XML file is read without AI (reading route
E_INVOICE_XMLinprovenance); for a PDF with XML attached the pages are read and the XML then replaces the numbers, dates and ids. The bill of lading may also be an electronic bill of lading as a DCSA eBL 3.0 transport document (a JSON file,bill_of_lading_fileorbill_of_lading_text): it is read from its data without AI (reading routeEBL_DCSA_JSON) and counts as an electronic record where the credit is subject to eUCP. Only the data is read: the authenticity of the record, the platform and the title holder are not checked. strictness:STANDARD(default) orSTRICT.amount_to_be_drawn(optional): the amount you claim under the credit, in its currency. When the invoice is above the credit, this amount decides (UCP 600 Art. 18(b)); without it and without a draft you get a warning to confirm.presentation_date(optional,YYYY-MM-DD): the day you will present the documents. With it the API checks the presentation period (UCP 600 Art. 14(c)) and that no document is dated later (Art. 14(i)); without it a note (presentationDateNotGiven) says neither was checked.instalment_numberandearlier_instalments_on_time(optional): under a credit with an instalment schedule, which instalment you present (1 for the first) and whether every earlier instalment was shipped and drawn in its period (trueorfalse). Without them the instalment is inferred from the shipment date (UCP 600 Art. 32).presented_counts(optional): a JSON object with the originals and copies you present per document, for example{"insurance_certificate": {"originals": 1, "copies": 0}}(the keys are the document fields of the request). With it the check tells you when the credit or UCP 600 asks for more (Art. 17, Art. 19 to 22 and 28(b)); without it you keep the notes that the count cannot be verified.earlier_drawn_amount,earlier_drawn_quantityandearlier_containers_shipped(optional): what earlier drawings under this credit took, in its currency and unit. With them the API checks the credit as a whole (total amount, total quantity, the last containers; UCP 600 Art. 18(b), 30 and 31). An invalid value is a 400 before anything is read or billed.presentation_medium(optional):PAPER,ELECTRONICorMIXED, how the documents go to the bank, withMIXEDpluselectronic_doc_types(comma separated, for exampleINVOICE,PACKING_LIST). It matters only when the credit is subject to eUCP, which the API reads from fields 40E, 46A and 47A.amendment_fileoramendment_text(optional): one amendment (an MT707 or an amendment letter from the bank) you have not answered yet, withamendment_statusPENDING,ACCEPTEDorREJECTED(PENDINGwhen left out). The documents are then checked against the credit as issued and the amended credit (UCP 600 Art. 10(c));amendment_benchmarkin the result says which one the findings are measured against. A status without an amendment is a 400.- If a required document is missing, the API answers with 400 before any document is processed or billed.
- The credit is read first, then every other document at the same time, not one after the other. The response comes as soon as the slowest document has been read.
curl -X POST https://api.docaccord.com/api/v1/lc-checks \
-H "Authorization: Bearer da_live_YOUR_KEY" \
-H "Idempotency-Key: shipment-4711-presentation-1" \
-F "lc_file=@lc.pdf" \
-F "invoice_file=@invoice.pdf" \
-F "bill_of_lading_file=@bill_of_lading.pdf" \
-F "packing_list_file=@packing_list.pdf" \
-F "strictness=STANDARD"{
"id": 1842,
"report_number": "LCC-2026-001842",
"lc_reference": "LC-2026-0457",
"overall_status": "REJECTED_DISCREPANCIES",
"compliance_score": 85,
"findings": [
{
"rule_reference": "UCP 600 Art. 18(c) / ISBP 821 C3",
"severity": "CRITICAL_ERROR",
"affected_document": "Commercial Invoice #INV-2291",
"field_name": "Goods Description",
"found_value": "100% cotton men's shirts",
"expected_value": "100% cotton men's shirts, style X",
"explanation_code": "goodsDescExactMismatch",
"explanation_params": {},
"requires_acknowledgment": false,
"suggested_fix_code": "goodsDescExactMismatchFix",
"suggested_fix_params": { "lcText": "100% cotton men's shirts, style X" },
"affected_doc_type": "INVOICE"
}
],
"created_at": "2026-09-23T09:14:02.118204"
}overall_statusisPASSED_FOR_BANK,WARNINGS_FOUNDorREJECTED_DISCREPANCIES.severityisCRITICAL_ERROR,WARNINGorINFO.- Each finding names the rule reference, the affected document and field, and the found and expected values. Instead of a rendered sentence the API returns a stable
explanation_codewith parameters, so you can render the text in your own system. requires_acknowledgmentistruewhen a person must confirm the finding because the data alone cannot decide it.suggested_fix_codewithsuggested_fix_paramsis the suggested correction,affected_doc_typethe document type. The params of a published code never change; a new meaning gets a new code, and the old one stays in theGET /api/v1/finding-codescatalogue for 90 days.provenancesays what the check ran on:ruleset_version(the date of the rule set) and, per document, whether the DocAccord Reader read it (extraction_model, valuedocaccord-reader), whether an independent second reading ran (verify_model, valuedocaccord-reader), the reader version (prompt_sha) and the reading route (extraction_route). Both fields are empty when a document was typed rather than read. An old result stays explainable after the rules or the reader changed.- Use the
idto fetch the result again later viaGET /api/v1/lc-checks/{id}.
Retries without double billing
- Optional header
Idempotency-Key. If the same request is repeated with the same key, you get the stored result back: no second check, no second charge. - The same key with a different request returns 409. So does a second request while the first one with that key is still running.
- For uploaded files, the request is recognised by the content of the file (SHA-256), not its name. Use a new key for every new document set.
- Stored results are kept for at least 24 hours.
ERP pre-check
Check an invoice against the credit saved with a case at the moment your ERP creates it. Without the bill of lading, the packing list or the other documents: they come later. The result says what the bank would fault on this invoice before it leaves the house. The decision is the bank's.
- The flow: a colleague reviews the credit in the DocAccord app and chooses Save credit terms to a case. Your ERP then calls
POST /api/v1/lc-cases/{case_id}/document-checksfor every invoice. Find thecase_idwithGET /api/v1/lc-cases(narrowed withlc_referenceto the credit number). - The credit is not sent: the case's latest saved version is used.
creditin the result names the case, the version and the save date it was checked against. Without a saved version (or while saving is switched off on the server) the API answers 409 with a note on what to do, before anything is read or billed. A case of another account and a case that does not exist are both 404. - The invoice is required (
invoice_fileorinvoice_text, as on a check, including an EN 16931 e-invoice). Optional: the bill of lading (bill_of_lading_fileorbill_of_lading_text, including a DCSA eBL 3.0 JSON) and the sales order (order confirmation, proforma invoice, sales contract or purchase order) asorder_fileororder_text, or as a JSON object inorder_jsonthat your ERP builds from its own data and that is compared with no reading. Anorder_jsonhas at most 50,000 characters. Plusstrictness,amount_to_be_drawnandpresentation_dateas on a check. - No transport document is required. Whatever else field 46A of the credit asks for counts as not yet presented and gives no "document missing" finding. Every other rule runs on what you sent.
documents_checkednames the documents that were checked;overall_statusholds for those documents, not for a whole presentation. - The sales order is compared with the same saved credit (buyer, seller, goods, quantity, price, amount, currency, trade term, ports, delivery date, partial shipments, payment terms).
order_checklists every difference as a warning with the wording you can ask the buyer's bank to amend, and what was not compared and why. It is not part ofoverall_status. A value the reading marked as unsure is not compared; anorder_jsoncounts as your own statement. - Billing, scopes, rate limit and
Idempotency-Keyare those of POST /api/v1/lc-checks:lc:write, one check credit per request once everything is read, 60 requests an hour per key. The documents are read at the same time. The run counts in your included checks and credits, but does not appear as a whole check in the app's history and gets no certificate. No webhook is sent. - The request size limit on this route is the API's general one (21 MiB per request, each file up to 20 MB).
Find the case_id
curl "https://api.docaccord.com/api/v1/lc-cases?lc_reference=LC-2026-0457" -H "Authorization: Bearer da_live_YOUR_KEY"{
"storage_enabled": true,
"cases": [
{
"id": 4711,
"lc_reference": "LC-2026-0457",
"lc_expiry_date": "2026-11-30",
"latest_shipment_date": "2026-10-31",
"credit": {
"saved": true,
"version": 2,
"saved_at": "2026-09-24T08:12:44.101233",
"source": "credit_review",
"terms_available": true
}
}
]
}Example SAP: billing document released
# Sent by your SAP integration layer (SAP Integration Suite, or an ABAP class
# through an HTTP destination) when the billing document is released.
# 4711 is the DocAccord case id you stored on the sales order.
curl -X POST https://api.docaccord.com/api/v1/lc-cases/4711/document-checks -H "Authorization: Bearer da_live_YOUR_KEY" -H "Idempotency-Key: sap-billing-90001234" -F "invoice_file=@90001234.pdf" -F "order_json=<sales_order_5000123.json"{
"document_kind": "OTHER",
"order_number": "5000123",
"order_date": "2026-08-14",
"buyer": { "name": "Rotterdam AgriTrade B.V." },
"seller": { "name": "Rhine Valley Commodities GmbH" },
"currency": "EUR",
"amount": 248000.00,
"quantity": 49500,
"quantity_unit": "MT",
"stated_incoterm": "CIF",
"incoterm_place": "Rotterdam",
"delivery_date": "2026-09-05"
}- Field names of the standard A_SalesOrder OData service (check them against your own release):
SalesOrderbecomesorder_number,SalesOrderDatebecomesorder_date,TransactionCurrencybecomescurrency,TotalNetAmountbecomesamount,IncotermsClassificationbecomesstated_incoterm,IncotermsLocation1becomesincoterm_place,RequestedDeliveryDatebecomesdelivery_date. The buyer's name comes from the business partner ofSoldToParty, quantity and unit from the items (RequestedQuantity,RequestedQuantityISOUnit). - Leave a key out when your system does not know the value: it is not compared, and
order_check.not_comparedsays so. Never send anything that is not on the order.
Example CargoWise: documents on the shipment
# Sent by your integration when the commercial invoice and the bill of lading
# are attached to the shipment (for example through CargoWise's eAdaptor, or a
# workflow rule that calls your middleware). The bill of lading may be the PDF or
# a DCSA eBL 3.0 JSON record.
curl -X POST https://api.docaccord.com/api/v1/lc-cases/4711/document-checks -H "Authorization: Bearer da_live_YOUR_KEY" -H "Idempotency-Key: cargowise-S00012345-docs-1" -F "invoice_file=@commercial_invoice.pdf" -F "bill_of_lading_file=@ebl.json;type=application/json" -F "presentation_date=2026-08-20"Result
{
"id": 1843,
"report_number": "LCP-2026-001843",
"case_id": 4711,
"lc_reference": "LC-2026-0457",
"overall_status": "REJECTED_DISCREPANCIES",
"documents_checked": ["INVOICE"],
"credit": {
"case_id": 4711,
"case_reference": "LC-2026-0457",
"version": 2,
"saved_at": "2026-09-24T08:12:44.101233",
"source": "credit_review"
},
"findings": [
{
"rule_reference": "UCP 600 Art. 18(c) / ISBP 821 C3",
"severity": "CRITICAL_ERROR",
"affected_document": "Commercial Invoice #INV-2291",
"field_name": "Goods Description",
"found_value": "100% cotton men's shirts",
"expected_value": "100% cotton men's shirts, style X",
"explanation_code": "goodsDescExactMismatch",
"explanation_params": {},
"requires_acknowledgment": false,
"suggested_fix_code": "goodsDescExactMismatchFix",
"suggested_fix_params": { "lcText": "100% cotton men's shirts, style X" },
"affected_doc_type": "INVOICE"
}
],
"order_check": {
"findings": [],
"compared": ["buyer", "seller", "currency", "amount", "quantity", "incoterm", "delivery_date"],
"not_compared": [{ "key": "unit_price", "reason": "not_on_order" }],
"order_kind": "OTHER"
},
"created_at": "2026-09-29T09:14:02.118204"
}The findings have the same shape as on a check (explanation_code with params, suggested_fix_code). The report number starts with LCP instead of LCC.
Credit review
Review the credit itself the moment it arrives or while it is still a draft, when an amendment is still a phone call. The exporter gets the LC Workability Review, the importer the LC Pre-Issuance Review: one route, two views.
- The credit is required as
lc_file(PDF, scan, photo) orlc_text(the pasted MT700 with its MT701s), never both.sideisexporter(the default, the beneficiary's view, the LC Workability Review) orimporter(the applicant's view, the LC Pre-Issuance Review).is_draft=truemarks a draft the parties are still negotiating: the same checks run, but a finding is then a negotiating position rather than an amendment. - Optional: a sales order as
order_file,order_textororder_json(the same keys as on the ERP pre-check).order_checklists every difference from the credit as a warning, apart from the status, counts and score. - Optional:
case_id(one of your cases fromGET /api/v1/lc-cases) orsave_to_case=truesaves the terms as a version with the sourcecredit_review. Without acase_idthe case is found or opened by the credit's reference number. The terms are saved only when every field the reading doubted was confirmed by a person. The API cannot do that, so for a reading with a doubtcredit_saved.statusisunconfirmedand nothing is saved. A draft is never saved (422). Without a save request the route saves nothing andcredit_savedis null. - The response gives
overall_status(WORKABLE, REVIEW_ADVISED or BLOCKED),risk_bandandrisk_score(our own weighted count, not a probability and not a rating by any bank), the findings withexplanation_code, params and the severity for the other party, andadviceandcommercial, the commercial reading. The wording insuggested_clauseis English because it goes into a credit. When it is locked for your account after the one free reveal,wordingis null andlockedis true; the finding itself is never withheld. - Billing: no check credit, as in the app. The reading goes through the same gate as
POST /api/extractand the reading allowance for credits. A pasted MT700 is read too (the model reads it, a deterministic SWIFT reader reads the same text a second time and settles every difference); the review, the comparison with the order and the wording need no model. Scopes (lc:write), 60 requests an hour, theIdempotency-Key(its own scope) and the error format are those of POST /api/v1/lc-checks. The route is stateless: no id, no fetch route, no webhook. A retry with the same key returns the stored answer without reading again.
# The LC Workability Review (side=exporter) of a credit that just arrived.
# Use side=importer for the LC Pre-Issuance Review of a draft you are about to
# send to your bank. lc_text takes the pasted MT700 instead of a file.
curl -X POST https://api.docaccord.com/api/v1/lc-reviews -H "Authorization: Bearer da_live_YOUR_KEY" -H "Idempotency-Key: credit-LC-2026-0457-review-1" -F "lc_file=@lc.pdf" -F "side=exporter" -F "is_draft=false"{
"lc_reference": "LC-2026-0457",
"side": "exporter",
"is_draft": false,
"overall_status": "BLOCKED",
"error_count": 1,
"warning_count": 1,
"risk_band": "MEDIUM",
"risk_score": 33,
"findings": [
{
"id": "IR-001",
"rule_reference": "L/C Field 31D",
"severity": "CRITICAL_ERROR",
"field_name": "Expiry date (31D)",
"explanation_code": "irAlreadyExpired",
"explanation_params": { "expiry": "2026-09-01" },
"requires_acknowledgment": false,
"counterparty_severity": "CRITICAL_ERROR",
"text_variant": null,
"suggested_clause": null
},
{
"id": "IR-003",
"rule_reference": "UCP 600 Art. 28(g)",
"severity": "INFO",
"field_name": "Documents required (46A)",
"explanation_code": "irInsuranceRisksUnstated",
"explanation_params": { "document": "Insurance Certificate" },
"requires_acknowledgment": false,
"counterparty_severity": "INFO",
"text_variant": null,
"suggested_clause": {
"clause_id": "LC-INS-02",
"wording": "The insurance line in field 46A is amended to state the risks to be covered, ...",
"locked": false,
"rule_reference": "UCP 600 Art. 28(g)",
"guidance_code": "clause.insuranceRisks"
}
}
],
"advice": [
{ "code": "thirdPartyDocuments", "tone": "COST", "params": { "count": 2, "list": "Certificate of Origin; Insurance Certificate" }, "cost": 300.0, "cost_currency": "EUR" }
],
"commercial": null,
"order_check": null,
"credit_saved": null,
"ruleset_version": "2026-09-29",
"created_at": "2026-10-01T09:14:02.118204+00:00"
}Webhooks
Instead of polling for a finished check, your system receives a signed notification. Delivery runs after the API response is sent and never delays it.
check.completed: a check has been stored.datacarries the same result as the POST /api/v1/lc-checks response. The ERP pre-check sends no webhook: its response is the result.check.failed: a check could not be completed.datacarrieslc_referenceandreason. Any check credit charged for it is refunded.- https URLs only, on a host that resolves to public internet addresses only (private, loopback and link-local addresses are refused, at registration and before every delivery). Up to 5 active endpoints per account, each subscribed to one or both events.
- Endpoints are registered by a signed-in user of your account, not with an API key, so a leaked key cannot add new delivery targets.
- On registration you receive a signing secret (
whsec_...). It is shown once.
POST /your/webhook/path
Content-Type: application/json
X-DocAccord-Event: check.completed
X-DocAccord-Timestamp: 1790154842
X-DocAccord-Signature: sha256=5f0c1d...
{"event":"check.completed","data":{"id":1842,"report_number":"LCC-2026-001842",...}}Verify the signature
- Signature = HMAC-SHA256 with your secret over
<timestamp>.<raw body>, sent asX-DocAccord-Signature: sha256=<hex>together withX-DocAccord-TimestampandX-DocAccord-Event. - The timestamp is part of the signed material. Reject deliveries outside your own tolerance so a captured request cannot be replayed later.
- Verify against the raw request body bytes before parsing JSON, and compare in constant time.
import hashlib
import hmac
import time
TOLERANCE_SECONDS = 300
def verify(secret: str, headers: dict, raw_body: bytes) -> bool:
timestamp = headers["X-DocAccord-Timestamp"]
signature = headers["X-DocAccord-Signature"]
if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
return False
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(
secret.encode(), signed, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Delivery and retries
- A response with a status below 300 counts as delivered. Timeout per attempt: 10 seconds.
- On 5xx or network errors, up to 5 attempts with exponential backoff (1, 2, 4, 8 seconds). A 3xx or 4xx response is not retried, and redirects are not followed.
- Every delivery is logged per endpoint: attempts, last status code, last error (for example
timeout,connection failedorHTTP 503). - Webhooks are a notification, not a dependency.
GET /api/v1/lc-checks/{id}stays the authoritative source if a delivery does not arrive.
Limits and billing
- API access is included in the Enterprise plan. Other plans cannot create keys, and calls are refused with 403.
- A check through the API is billed exactly like a check in the browser: first from the period's included checks, then from check credits.
- When included checks and credits are used up, POST /api/v1/lc-checks answers with 402.
GET /api/v1/usageshows where you stand at any time. - Enterprise accounts can buy check credit packs of any quantity on the Billing page and switch on automatic top-up: when the credit balance falls below a level you choose, the quantity you chose is bought and charged to your subscription's payment method (Paddle sends the invoice), up to a monthly limit you set. Credits arrive once Paddle confirms the payment, usually within a minute. A 402 while a top-up is running carries Retry-After, and
GET /api/v1/usageshows auto_topup_enabled and topup_pending. - ping, usage, the list of cases, fetching past results and the credit review (POST /api/v1/lc-reviews) spend no check credit. An ERP pre-check (POST /api/v1/lc-cases/{case_id}/document-checks) spends one check credit once all its documents are read, exactly like a check; if reading fails it costs nothing.
- Rate limit: 60 requests per hour per API key on POST /api/v1/lc-checks, POST /api/v1/lc-reviews and POST /api/v1/lc-cases/{case_id}/document-checks, 200 per hour per key on every other authenticated route. Above that the API answers with 429.
- One check reads at most 15 documents, not counting the credit and its amendment. A request with more is refused with 429 before the first one is read; split the presentation into two checks. Documents of requests that fail after they were read count until a check completes, or for at most 24 hours after the first of those readings; a request whose documents no longer fit next to them gets 429 with Retry-After. Parallel requests do not count against each other.
- Every API key has a daily limit for AI document reading that starts over at midnight UTC. At 80 percent the account's email address gets one warning a day; once the limit is reached the API answers 429 with Retry-After until midnight UTC. Close to the limit, a request can also get 429 with Retry-After 60 while other requests with the key are still running; retry it after a minute. Checks already running are not interrupted. Write to us if you need a higher limit.
Error codes
Errors come back as JSON with a human-readable detail field.
| 400 | Required document missing, file and text for the same document, or file not readable |
| 401 | Key missing, unknown, revoked or expired |
| 402 | Included checks and check credits used up |
| 403 | Account not on the Enterprise plan, the key lacks the scope, or AI document reading is paused for the account (contact us) |
| 404 | Check or case does not exist or does not belong to your account |
| 409 | Idempotency-Key reused with a different request, or the first request is still running; on the ERP pre-check also: the case holds no saved credit (or saving credit terms is switched off on the server) |
| 413 | File larger than 20 MB, more than 100 pages or more than 30 scanned pages, an image above 100 megapixels, pasted text above 1,000,000 characters, or a scan that took too long to process |
| 422 | A field fails validation, a page (other than a photo or scan) is larger than A0, or the PDF is password protected |
| 429 | Rate limit exceeded, or the key's daily limit for AI document reading reached (until midnight UTC; Retry-After says how long to wait), or more documents than one check reads (15, readings of failed requests included) |
| 503 | Document reading is unavailable right now (an outage at the AI provider, or paused for a few minutes because of unusually high load); Retry-After says how long to wait |
Environments
The prefix tells you at a glance which environment a key belongs to. A key only works in the environment that issued it.
| Environment | Base URL | Key prefix |
|---|---|---|
| Live | https://api.docaccord.com | da_live_ |
| Sandbox | https://api-staging.docaccord.com | da_test_ |
Stability and schema
- A breaking change to a v1 response is never made in place. It ships as /api/v2, and v1 keeps working.
- If a v1 endpoint is retired, we give at least 90 days' notice: a Sunset header (RFC 8594) on that endpoint, and a direct notice to every partner whose active key used it in the preceding 30 days.
- The partner API's OpenAPI schema is published at
https://api.docaccord.com/api/v1/openapi.json, ready to import into Postman or any OpenAPI tool.
Put the check inside your own system
API access and webhooks are part of the Enterprise plan. Talk to us about your volume and your integration.