Skip to main content

Propose a booking

This is the Docbooks core. Product-wise: the customer hands you a receipt; you want a posting proposal, not a pile of OCR fields. You POST the file to Docbooks only. Architecturally Docbooks runs the analysis behind the scenes — it forwards your Bearer token, which must carry docmind.capture — and then books.

You get 202 with a bookingJobId in QUEUED status. Wait for BOOKING_FINALISED, then GET. Do not POST the file to Docmind first.

POST/api/docbooks/v1/workspaces/{workspace_id}/bookings
Warning

Two things must be true before the first booking. The workspace must be provisioned against an extraction workspace (unprovisioned → 409), and it must have an effective chart of accounts, its own or its organisation's (none → 400). Use the Docbooks workspace id from onboarding, not a Docmind workspaceId. Chart of accounts and settings · Combine analysis and booking.

Keep the original file bytes. Booking is a multipart POST to Docbooks — never a Docmind documentId.

Authentication​

Authorizationstringheaderrequired

Bearer token with scopes docbooks.bookings and capture (docmind.capture / deprecated idp.capture). Submit requires both on either path, even the pre-extracted one; the token is only forwarded to the extractor on the file path.

Path​

workspace_idstring (ULID)pathrequired

Docbooks workspace.

Multipart​

Send exactly one of file or extraction.

filefilebody

PDF or image to book. Docbooks forwards it to the extractor for OCR and extraction. Over the upload limit (200 MB) is 413 FILE_TOO_LARGE; anything other than a PDF or image is 415 UNSUPPORTED_MEDIA_TYPE.

extractionstring (JSON)body

A Docmind DocumentResponse you already hold, as JSON. Docbooks parses it and skips docmind entirely — no upload, no OCR wait. An invalid or unbookable extraction is a 400. Sending both parts, or neither, is 400 INVALID_SUBMISSION.

metadatastring (JSON)bodyrequired

JSON object: required actor.extRef; optional extRef; optional processingInstruction (split.enabled, capture forwarded to Docmind as options, optional settings override).

After 202​

Wait for BOOKING_FINALISED, then:

GET/api/docbooks/v1/bookings/{booking_job_id}

The GET needs the same two scopes as submit — Docbooks reads the extraction from Docmind with your token. Optional query include=extraction embeds the raw extraction on each record.

The report carries a job-level status and one BookingRecord per document in data — one for a single upload, several when the upload was split.

statusWhat it meansWhat you do
QUEUEDAccepted, not started.Wait for the webhook.
RUNNINGExtraction or generation in flight.Wait for the webhook.
COMPLETEDBalanced booking, no open questions.Post it, or show it for a one-click confirm.
REVIEW_REQUIREDA booking exists but something needs a human — see reviewReasons.Route to review.
FAILEDNo booking was produced — see error.Do not retry blindly; fix the input or the chart.

Each record also carries the same states in its own status, plus reviewReasons (empty when COMPLETED) and reasoning (confidence 0–1, explanation, appliedRules). Postings get a status and vatAccountable of their own, so one weak posting does not hold back its siblings.

A posting's bookingText reads [document type] [sender] [MM.YY] [largest line item] — the document type in German (Rechnung, Gutschrift, Beleg), the line item cut at its first ( — and leaves out any part the document lacks. Its vatCode comes from the revenue and expense accounts it books, plus the VAT leg under vatPosting: EXTRA; a Kreditoren or bank counter leg never decides it. The code is UNKNOWN when those accounts carry no code, when they carry conflicting codes, or when a NONE account books a document that carries VAT — in the last two cases the posting's reasoning.explanation says why.

discarded lists the posting groups that did not make it into postings — empty when nothing was dropped. Each entry has a reason (AMOUNT_UNDETERMINED, VAT_LEG_SHAPE_INVALID, LEGS_UNRESOLVED, LOW_CONFIDENCE) and either the proposedLegs that never resolved to an account on your chart, or the posting that scored below the confidence floor. Show it in the review lane so no part of the document disappears silently. A non-empty discarded always holds the booking at REVIEW_REQUIRED — even when the remaining postings balance and reconcile — with error.code POSTING_GROUP_DISCARDED unless a stronger reason already applies.

Every booking is reconciled against the document's own total. When that total cannot be read, the record is REVIEW_REQUIRED with error.code TOTAL_UNVERIFIABLE; when the postings do not add up to it, AGGREGATE_MISMATCH.

Tip

List bookings: GET /api/docbooks/v1/workspaces/{workspace_id}/bookings (pagination envelope; each item is a full job report).

Note

If split is enabled on the booking metadata, only the parent job fires the webhook. Children appear on the GET report.