Skip to main content
JSON is the only request media type on all three APIs; document upload operations additionally accept multipart data. Send the media type declared by the current operation reference; unsupported types return 415. Encode JSON as UTF-8: a Content-Type charset parameter is honoured only for the UTF-8, UTF-16, and UTF-32 family, any other charset is 415 unsupported_media_type. A request body is parsed strictly: invalid JSON, nesting deeper than 64 levels, non-finite numbers, unpaired surrogate escapes, and duplicate object members are 400 parse_error, and a declared Content-Length above the body ceiling is 413. Always send a Content-Length; a chunked request body without one is 411 length_required. Commands that declare no request body (rotate-secret, redeliver, Partner membership resend and cancel, the Mahnservice invoice actions) and every DELETE on the Case Management and Partner APIs reject one with 400 unexpected_body — including {}, [], and null; only an empty body is accepted, and a rejected DELETE deletes nothing. finalize is the exception: it accepts an empty body or exactly {}, and any member is 400 validation_error with unknown_field.

Canonical URLs

Send the documented trailing slash on every Case Management (/v2/) and Partner (/partner/v2/) request. Slashless roots and routes return JSON 404 for every method, including GET, HEAD, OPTIONS, and writes, with no redirect and no Location header. For example, /v2/info and /partner/v2/info are unavailable; use /v2/info/ and /partner/v2/info/. This contract is the same in production and the sandbox.

Data conventions

  • IDs are UUID strings named id. A path segment that is not a UUID answers 404 on every method, before authentication and without consuming rate-limit capacity.
  • Timestamps such as created_at and updated_at use RFC 3339 in UTC: the Case Management and Partner APIs render Z, the Mahnservice API +00:00. Accept both when parsing.
  • Single-line text fields (names, references, subjects, labels) reject control characters — including the line separators U+2028 and U+2029 — with 400 control_characters; invisible format characters are stripped and the text is NFC-normalized before validation. Multi-line fields (claim items[].description, legal_basis.description, dispute_reason, message body, request-to-client answers, the withdrawal reason) allow TAB, LF, and CR, store every line break as \n, and reject other control characters.
  • Most string fields (title, body, text, your_reference, street, issuing_authority, file_number) accept a JSON number and store its decimal text; the multi-line claim fields and the withdrawal reason reject non-strings with 400 invalid. Booleans such as creditor_obligation_fulfilled, is_disputed, and webhook enabled must be JSON true/false — "true", 1, and "" are 400 invalid.
  • YYYY-MM-DD dates and numeric identifiers (postal codes, IBANs, limit, offset) accept ASCII digits only; other Unicode digits are 400 invalid (query parameters: invalid_parameter).
  • Calendar-date field names end in _date.
  • Money is a decimal-string value in EUR, for example { "value": "125.50", "currency": "EUR" }.
  • Collection members are updated with PATCH. PUT exists only for a singleton sub-resource that you create or replace in place, currently the enforceable title of a claim in a titled order (PUT /v2/claims/{claim_id}/enforceable-title/).
  • Unknown fields, read-only fields, and unknown nested fields are rejected with 400 instead of being ignored — on the Mahnservice API as well. Unknown query parameters are 400 too.
For Mahnservice amount, currency, address, email, and text formats, see its input field rules.

Mahnservice request details

Use the trailing slash on /mahnservice/v1/ routes. A GET or HEAD without it redirects with 301; a write without it returns 404. All Mahnservice responses, including schema and documentation responses and the JSON 404 for an unmatched route, include X-Paywise-Request-Id, X-Paywise-Environment, and Cache-Control: private, no-store. Unknown body fields return 400 unknown_field, read-only fields 400 read_only_field, and unknown query parameters 400 unknown_parameter. For pagination and rate limits, use the shared pagination guide and public limits.

Idempotency keys

Every effectful POST on the Case Management and Partner APIs requires an Idempotency-Key header. The key marks one logical command, so that a retry after a timeout or an ambiguous outcome can never create a second order, payment, or webhook subscription:
  • Generate one opaque key (a fresh UUID works) per logical command and retain it until the outcome is known.
  • Retrying an ambiguous result means resending the same key with the same body. A completed command replays its original response with Idempotency-Replayed: true; the same key with a different body returns a typed 409.
  • A new logical command always gets a new key — reusing yesterday’s key for today’s write turns a real command into a replay.
  • GET, PATCH, PUT, and DELETE take no key and ignore one that is sent: reads are naturally safe to repeat, and a partial update resent unchanged converges on the same state (it does not even bump updated_at or the ETag).
The Mahnservice API takes no Idempotency-Key header; there, submitting an invoice is idempotent on its invoice_number instead. See Errors and safe retries for the full retry semantics, key scoping, and the idempotency error codes.

Partial updates

With PATCH, an omitted field remains unchanged. null clears only a field documented as nullable. An empty object still counts as supplied and is validated. When an owned array is supplied, it replaces the complete array; children omitted from the replacement are deleted if nothing else references them. Omit the array to leave it unchanged.
Every response, including an error, has fresh X-Paywise-Request-Id and X-Paywise-Environment headers. Authenticated data uses Cache-Control: private, no-store; only the immutable legal-form catalog may be cached privately for one day.

Conditional requests

On the Case Management and Partner APIs every single-resource read (GET …/{id}/) and every PATCH or PUT response carries a strong ETag that identifies the returned representation. Lists, POST responses, downloads, and idempotent replays carry none. The Mahnservice API publishes no ETag and ignores If-Match and If-None-Match.
  • Skip an unchanged re-read. Send the last ETag as If-None-Match. When the representation is unchanged the response is 304 Not Modified with an empty body and the same ETag; otherwise you receive the full 200 body.
  • Guard a write against a concurrent change. Send the ETag you last read as If-Match on PATCH, PUT, or DELETE. When the current representation no longer matches, the request fails with 412 and the code precondition_failed, and nothing is written. Re-read the resource, merge, and retry with the fresh validator. If-Match: * only requires that the resource exists.
Both headers are optional; a request without them behaves as before. Weak validators (W/"…") never satisfy If-Match; the validator is compared literally against the current representation, so keep the quotes exactly as received. If-None-Match is evaluated on GET only. On orders, claims, the enforceable title, the rental agreement, and messages, the If-Match check runs again after the write has locked the resource, so of two writers that both hold the current ETag the second gets 412. On every other resource the check is optimistic: it runs before the write is locked, so two such writers can both succeed and the last one wins — re-read after a 200 if you need to confirm the final state. A write route whose members are read elsewhere (DELETE /v2/claims/{claim_id}/payments/{id}/ reads at GET /v2/payments/{id}/) has no representation to compare and answers If-Match with 412; omit the header there.

Recover from invalid requests

For 400, read the machine-readable top-level code and each nested field error, correct only the rejected fields, and submit a new request. For 415, switch to the media type named by the operation. Do not turn a rejected PUT into a full-object PATCH; send only the intended changes. See update a Partner company for a JSON partial update and create a claim document for a current upload operation.