> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paywise.de/llms.txt
> Use this file to discover all available pages before exploring further.

# Invoice lifecycle

> Submit an invoice held, correct it before release, start dunning explicitly, and settle it through payment, cancellation, or write-off.

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.

```text theme={null}
POST /invoices/                  → held        (recorded, never dunned)
PATCH /invoices/{uuid}/          → held        (correct what you submitted)
POST /invoices/{uuid}/documents/ → held        (scan the PDF asynchronously)
GET /invoices/{uuid}/documents/{document_uuid}/ → pending → ready | rejected | failed
POST /invoices/{uuid}/release/   → dunning     (the process is created here)
POST /invoices/{uuid}/payments/  → partial or settling
POST /invoices/{uuid}/cancel/    → cancelled
POST /invoices/{uuid}/write-off/ → written off
```

`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:

```http theme={null}
PATCH /mahnservice/v1/invoices/8f1d0f3a-0b4a-4a5c-9f2e-6a7f1b2c3d4e/ HTTP/1.1
Host: api.paywise.de
Authorization: Bearer <api-key>
Content-Type: application/json

{"amount": "1250.00", "due_date": "2026-09-15"}

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "8f1d0f3a-0b4a-4a5c-9f2e-6a7f1b2c3d4e",
  "invoice_number": "RE-2026-0184",
  "amount": "1250.00",
  "due_date": "2026-09-15",
  "dunning_state": "held",
  "balance": "1250.00",
  "paid_amount": "0.00",
  "overpaid": false
}
```

`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](/api-docs/essentials/requests-and-responses)
for the shared JSON and error conventions.

| Field | Rule |
| - | - |
| Dates (`document_date`, `due_date`, `value_date`) | `YYYY-MM-DD` with ASCII digits (`400 invalid` otherwise), between `1900-01-01` and ten years ahead; `document_date` ≤ `due_date`; a payment's `value_date` cannot be in the future. |
| `amount` (invoice and payment) | Decimal string matching `^[0-9]{1,10}(?:\.[0-9]{1,2})?$` (preferred — no exponent, sign, or surrounding whitespace, matched against the whole value) or a JSON number with at most two decimals; greater than zero. Responses always render a string. `paid_amount` and `balance` allow up to twelve integer digits. |
| `currency` | `EUR`, `USD`, `GBP`, or `CHF` (case-insensitive on input, upper-case in responses); defaults to `EUR`. Changing it after payments were reported is `409 payments_reported`. |
| `address` | `street`, `zip_code`, and `city` are required together; `country` is a two-letter ISO 3166-1 code, default `DE`. |
| `email` | At most 254 characters; the domain is lower-cased, and dotless, IP-literal, and paywise domains — including IDNA or look-alike spellings of them — are rejected (`own_domain_email`). |
| Text fields | Strings only (no numeric coercion), single line: control characters are `400 control_characters`, format characters are stripped, NFC applied. Blank optional strings are stored and returned as `null`. |

## The correction window closes at release

`PATCH` answers `409` once the invoice has left the held state:

| Code | Meaning |
| - | - |
| `already_released` | Dunning has started. Notices already sent quote the amount and the due date, so an edit would silently rewrite what the debtor was told. Cancel the invoice and submit a corrected one. |
| `invoice_paid`, `invoice_cancelled`, `invoice_written_off` | The claim is settled. These states have their own verbs and no longer describe an open receivable. |
| `invoice_archived` | The invoice is archived. |
| `invoice_in_inkasso` | The claim has been handed to debt collection; the collection case now owns it. |
| `payments_reported` | You tried to change `currency` after payments were reported against the invoice. |
| `duplicate_invoice_number` | Another invoice already uses the number you renamed to. |
| `not_api_invoice` | The invoice belongs to a connected bookkeeping system. See [Ownership and access](/api-docs/mahnservice-api/concepts/ownership-and-access). |

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](/api-docs/mahnservice-api/concepts/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`.

| Invoice state | `PATCH` | document upload | `release` | `cancel` | `write-off` | `pause` | `resume` | payment |
| - | - | - | - | - | - | - | - | - |
| held | `200` | `201` | `200` | `200` | `200` | `409 not_released` | `409 not_released` | `201` |
| released, dunning active | `409 already_released` | `409 already_released` | `200` (idempotent) | `200` | `200` | `200` | `409 not_paused` | `201` |
| held, archived | `409 invoice_archived` | `409 invoice_archived` | `409 invoice_archived` | `200` | `200` | `409 not_released` | `409 not_released` | `201` |
| released, archived | `409 already_released` | `409 already_released` | `200` (idempotent) | `200` | `200` | `200` | `409 not_paused` | `201` |
| paid | `409 invoice_paid` | `409 invoice_paid` | `409 invoice_settled` | `409 invoice_paid` | `409 invoice_paid` | `409 invoice_paid` | `409 invoice_paid` | `201` |
| cancelled | `409 invoice_cancelled` | `409 invoice_cancelled` | `409 invoice_settled` | `200` (idempotent) | `409 invoice_cancelled` | `409 invoice_cancelled` | `409 invoice_cancelled` | `409 invoice_cancelled` |
| in debt collection | `409 invoice_in_inkasso` | `409 invoice_in_inkasso` | `200` (idempotent) | `409 invoice_in_inkasso` | `409 invoice_in_inkasso` | `409 invoice_in_inkasso` | `409 invoice_in_inkasso` | `201` |

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](#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/`](/api-docs/case-management-api/reference/webhooks/create-webhook).
These subscriptions belong to your company. Partner integrations use
[Partner-owned subscriptions](/api-docs/partner-api/concepts/webhook-ownership#receive-case-and-mahnservice-events).

Use the exact keys below in the subscription's `events` list. See
[Webhooks](/api-docs/essentials/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.

| Event key | When it is sent |
| - | - |
| `invoice.created` | A Mahnservice invoice was created through the API or imported from bookkeeping. |
| `invoice.paid` | A Mahnservice invoice was marked paid after a reported payment or bookkeeping synchronization. |
| `invoice.cancelled` | A Mahnservice invoice was cancelled (Storno). |
| `invoice.written_off` | A Mahnservice invoice was written off (Ausbuchung). |
| `dunning.level_advanced` | Dunning advanced to the next level after dispatch or handover for manual delivery when no channel could deliver. It does not fire on the final level, where the process completes. |
| `dunning.handed_to_collection` | A Mahnservice invoice was handed over to debt collection (Inkasso) — including an invoice submitted through this API without any bookkeeping integration. |

`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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.