Skip to main content
An invoice submitted through this API passes through exactly one gate that does not exist in any other intake path: it is held until you release it.
dunning_state reports where an invoice stands. It is held from submission until release, then the state of the dunning process (pending, active, paused, completed, inkasso), and finally the terminal value (paid, cancelled, written_off).

Held is a deliberate stop, not a queue

A held invoice has no dunning process at all. Nothing advances it: not a background sweep, not activating the dunning flow in the portal, not another invoice of the same debtor. Only POST /invoices/{uuid}/release/ creates the process, and that call is the moment paywise starts writing to the debtor. This is what makes a two-system integration safe. You can mirror your whole open-item list into paywise without committing to dunning any of it, decide per invoice, and release on your own schedule.

Submission is idempotent, so correction has its own verb

POST /invoices/ is idempotent on invoice_number within your company. A repeat submission of a number that already exists returns 200 with the existing invoice and applies none of the fields you sent — the body must still be well-formed, but differing values are ignored rather than compared with the stored invoice; a genuinely new number returns 201. Retrying an ambiguous POST is therefore always safe. The replay stops where the number no longer names an open receivable: a number that belongs to a cancelled, written-off, or archived invoice is 409 invoice_cancelled, invoice_written_off, or invoice_archived — submit the corrected invoice under a new invoice number. Paid, open, and overdue invoices keep the 200 replay. The Mahnservice API does not read an Idempotency-Key header. The consequence is that resubmitting is not a way to change anything. To correct an invoice you already submitted, use PATCH while it is still held:
PATCH is partial: only the fields you send change. invoice_number, amount, currency, due_date, document_date, your_reference and debtor are correctable. The company and the credential context are never writable, and dunning_state, archived, has_document and balance change only through their own endpoints. There is no PUT and no DELETE: a whole-body replace would make omitting a field destructive, which is the opposite of what a correction wants. Do not invent a second invoice number to fix a typo. The invoice number is your own, from your accounting system, and paywise prints it in the notices the debtor receives. Correcting the record in place keeps your books and the debtor’s correspondence on the same number. Correcting invoice_number itself is allowed, and it moves the idempotency key with it: after renaming RE-1 to RE-2, a submission of RE-1 creates a new invoice and a submission of RE-2 is the idempotent repeat. Renaming onto a number that already exists is refused with 409 duplicate_invoice_number, and nothing is changed. Correcting due_date re-derives whether the invoice counts as open or overdue, exactly as the same date would have at submission.

Input field rules

These rules apply when writing Mahnservice debtors, invoices, and payments. See Requests and responses for the shared JSON and error conventions.

The correction window closes at release

PATCH answers 409 once the invoice has left the held state: The invoice PDF has the same window: POST /invoices/{uuid}/documents/ attaches or replaces it while the invoice is held, and answers 409 already_released afterwards; on a settled or archived invoice it answers 409 invoice_paid, invoice_cancelled, invoice_written_off, or invoice_archived, on a claim in debt collection 409 invoice_in_inkasso, and on a bookkeeping invoice 409 not_api_invoice. The document is enclosed with the dunning notices, so it is fixed at the moment the first notice can go out. A PATCH whose values equal the stored ones writes nothing: updated stays put and no history entry is recorded. The upload responds 201 with a document whose status is pending. Poll its detail endpoint until it is ready, rejected or failed. has_document on the invoice is only true for a ready document. An attached document that is not ready blocks release with 409 document_not_ready; releasing without an attachment is allowed. See Invoice documents for the response shape, download route and replacement workflow.

Release

POST /invoices/{uuid}/release/ creates the dunning process. It is idempotent: releasing an already-released invoice returns the current state rather than starting a second ladder. The process starts active when the company’s dunning flow (Mahnlauf) has been activated in the portal, and pending until then — a pending process is promoted automatically at activation. A company-wide or debtor-level pause parks it as paused. When a debt-collection case already covers the claim, the process is created directly in inkasso. Release is refused with 409 no_default_dunning_flow when no default dunning flow is configured; configure one in the portal first. It is also refused for an archived held invoice (invoice_archived) and for a settled invoice (invoice_settled) — including one that was released and then paid, where the settled state outranks the idempotent replay.

Payments and terminal states

POST /invoices/{uuid}/payments/ records a payment. A partial payment lowers the reported balance while dunning continues over the full invoice amount; once the reported total covers the amount, the invoice is marked paid and dunning stops. The invoice exposes the read-only paid_amount (sum of all reported payments) and overpaid (true when paid_amount exceeds amount); over-payment is recorded, not refused, and balance never drops below zero — reconcile a surplus in your own system. Payment reporting is retry-safe: an identical repeat (same amount, value_date and a non-empty reference, compared case-insensitively) returns 200 with the existing payment instead of counting it twice. An identical payment submitted without a reference is refused with 409 duplicate_payment, because a retry and a genuine second identical instalment are otherwise indistinguishable — resubmit with a distinct reference to record a real second payment. POST /invoices/{uuid}/cancel/ and POST /invoices/{uuid}/write-off/ end the claim and stop its dunning. Both are idempotent, and both answer 409 when the invoice already reached a different terminal state or is in debt collection (invoice_in_inkasso). dunning/pause and dunning/resume answer 409 invoice_paid, invoice_cancelled, or invoice_written_off on a settled invoice, 409 not_released while the invoice is still held, and 409 invoice_in_inkasso once the claim is in debt collection. resume is also refused with 409 company_paused while dunning is paused for the whole company (by you in the portal or by paywise); lift that hold first. A debtor-level hold does not block resume: the process resumes for that invoice despite the hold. All five commands take no request body: null, false, "", [], and {} are 400 unexpected_body as well.

Which conflict wins

Every mutating command checks the invoice in the same order and answers the first conflict it meets, so the code you receive is deterministic:
  1. not_api_invoice — the invoice belongs to a connected bookkeeping system.
  2. The idempotent 200 — cancel or write-off repeated on the same terminal state, release on any released invoice.
  3. The settled codes invoice_paid, invoice_cancelled, invoice_written_off (release reports invoice_settled; payment reporting is exempt from paid).
  4. invoice_in_inkasso.
  5. The correction window: already_released (held-only commands: PATCH, document upload) or not_released (released-only commands: pause, resume).
  6. invoice_archived (PATCH, document upload, release).
  7. The command’s own codes — payments_reported, duplicate_invoice_number, document_not_ready, no_default_dunning_flow, duplicate_payment, already_paused, invalid_status, not_paused, company_paused, complaint_requires_letter, stripe_connection_revoked.
A written-off invoice behaves like a cancelled one with invoice_written_off (write-off is the idempotent repeat instead of cancel). A bookkeeping invoice answers 409 not_api_invoice in every cell. A 409 never changes the invoice; updated is untouched.

Read the dunning state

GET /invoices/{uuid}/dunning/ returns the process state, the pause reason, the current level, the next action date, the configured flow, and the history of notices already sent with their fees. Once the process has ended — paid, cancelled, written_off, completed, or inkasso — current_level and next_action_date are null, and pause_reason and paused_at are cleared even when the invoice was settled while paused. Use the Mahnservice webhook events to learn about invoice creation, payment, cancellation, write-off, dunning-level advancement, and handover to collection, then fetch the current resource. For reconciliation and changes without a dedicated event, poll this endpoint or list invoices and use updated as a change marker. It advances on every change you can observe through the API — a correction, a reported payment, a state transition, a document reaching ready, rejected, or failed — and never on internal retries or on a no-op PATCH. Invoice lists are offset paginated and newest first; there is no updated_since filter on this API. ordering sorts GET /mahnservice/v1/invoices/ by created, due_date, amount, or invoice_number and GET /mahnservice/v1/debtors/ by created or customer_number; a - prefix sorts descending, the default is -created, and ties use a stable internal row key rather than the public UUID. An unknown term is 400 on ordering with code invalid_parameter; ordering on a detail or sub-resource route is 400 unknown_parameter.

Webhook events

Subscribe to invoice and dunning events through the company webhook system in the developer portal or POST /v2/webhooks/. These subscriptions belong to your company. Partner integrations use Partner-owned subscriptions. Use the exact keys below in the subscription’s events list. See Webhooks for the common event envelope, signature verification, retries, and troubleshooting. These events concern pre-collection invoices and dunning in the Mahnservice API, including invoices imported from bookkeeping. Every event in this group uses InvoiceWebhookEvent; its data contains company_id, invoice_id, and invoice_url. Fetch invoice_url under /mahnservice/v1/invoices/{id}/ with a credential authorized for the Mahnservice API. invoice.created, the settlement events of a single invoice, and dunning.level_advanced are recorded in the same transaction as the change that raises them, so they are delivered even when the delivery worker is briefly unavailable. A pre-existing collection case that flips an invoice to inkasso records dunning.handed_to_collection in that same transaction. For a new handover, the transfer transaction commits a durable handover record; a relay records dunning.handed_to_collection under that record’s stable event ID shortly afterward; its created_at is the relay time, and retrying the relay does not create a second event.