Skip to main content
This quickstart is designed for the sandbox and uses a company-bound Bearer key. The Python examples use https://api-sandbox.paywise.de directly; replace the inline PAYWISE_API_KEY placeholder with a sandbox key (pw_sbx_…) created in the developer portal. Other language tabs read the matching environment variables. The request tabs below are complete, copyable examples for individual operations. The final section runs the whole flow end to end once in Python. There is no Idempotency-Key header on this API: submitting an invoice is idempotent on its invoice_number (per company and environment), so a repeat submission returns 200 with the existing invoice instead of creating a duplicate.

Before you start: save a sandbox Mahnlauf

Mahnservice configuration is sandbox-native and is not copied from production. Company profile, the entering user’s membership, company-level notification recipients, and that user’s own notification recipients are mirrored; a synthetic sandbox entitlement is granted independently of a production purchase. Enter the sandbox from the production portal under Für Entwickler → Zur Sandbox, then open Mahnservice → Erste Schritte → Mahnlauf einrichten. Configure the flow, proceed to step 4 (Speichern), and choose Mahnlauf speichern. The sandbox saves a default flow without checkout, a paid subscription or a SEPA mandate. Bookkeeping integration is unnecessary; this API supplies the invoices. Saving creates the default configuration; activation is a separate step. Choose Weiter to return to Erste Schritte, then Mahnlauf prüfen und aktivieren when the page shows Alles bereit!. This starts the sandbox ladder; outbound notices are captured in the E-Mail-Log instead of delivered. Until you activate the flow in the portal, API release creates a pending dunning process. GET /mahnservice/v1/dunning-flows/ lets you inspect the configuration. If release reports 409 no_default_dunning_flow, complete the wizard in the same sandbox company as your key. In production, the wizard also saves the configuration before opening Weiter zur Zahlung. Abandoning checkout keeps the flow so checkout can be resumed through Abo abschließen on Erste Schritte, but a payment method and an active subscription are still required for production use. Sandbox setup does not change production billing.

1. Find or create the debtor

Creating a debtor is not idempotent — a repeat call creates a second debtor row. Keep the returned id in your own customer record. If it is missing, look up your customer before creating another:
cURL lookup
customer_number is an exact, case-sensitive match; email is an exact, case-insensitive match against the debtor’s displayed email. If you supply both, they are combined with AND. Results stay scoped to your company and credential’s data lane and use normal count, next and results pagination. Omit an unused filter; blank or whitespace-only values, customer numbers over 50 characters and malformed email values return 400 with the field name. Surrounding whitespace is trimmed from customer_number on lookup exactly as it is on create, so a padded lookup still finds the row you created. Reuse a matching debtor only after checking its identity. These fields are not unique: multiple matches need reconciliation, and a lookup followed by a create cannot prevent two concurrent workers creating duplicates. Serialize creation per customer in your integration and store the resulting id. The create examples below apply when there is no existing match.
The create response returns the debtor’s id directly — read it from the 201 body.

2. Submit the invoice

The invoice is created held: recorded and visible, but not dunned, and no dunning process exists yet. Use a fresh invoice_number for a new claim; a repeat submission of the same number returns 200 with the existing invoice and ignores every changed field. To fix wrong data while the invoice is held, use PATCH /mahnservice/v1/invoices/{uuid}/ instead of inventing a second number.
201 means a new invoice was created; 200 means this invoice_number was already submitted and the existing invoice was returned unchanged. Read the invoice’s id from the body — dunning_state is held.

3. Attach the invoice PDF and wait for scanning

If you want the original PDF enclosed with the notices, upload it while the invoice is held. The invoice documents guide shows the multipart request and the response shape. POST .../documents/ returns 201 with a document id and status: "pending"; it does not return an invoice. Poll GET .../documents/{document_uuid}/ until ready before releasing. Stop on rejected or failed, and upload a corrected replacement while held. Release returns 409 document_not_ready while an attached document is not ready. An invoice without an attachment can also be released. The complete Python example below includes the upload and a bounded poll.

4. Release the invoice for dunning

Release declares no request body. It creates the dunning process and is the moment paywise starts writing to the debtor. The process starts active when the company’s Mahnlauf is activated, pending until then. Release is idempotent — a second call returns the current state.

5. Read the dunning state

The dunning projection shows the state, the current level, the next action date, the configured flow, and the history of sent notices with their fees.
From here, report payments as they arrive (POST /mahnservice/v1/invoices/{uuid}/payments/), or end the claim with cancel or write-off. When the debtor never pays, the dunning ladder ends in the handover to collection (dunning_state: "inkasso", dunning.handed_to_collection). That handover needs no bookkeeping integration: paywise builds the collection case from the invoice, debtor, reported payments, and PDF you submitted here.

6. Runnable end-to-end workflow

Install requests, replace the inline PAYWISE_API_KEY placeholder, and set INVOICE_PDF_PATH to a real local PDF. Configure the sandbox Mahnlauf first as described above. The program reuses a customer by customer_number, uploads the PDF, and stops on a failed scan or after at most 60 polls. Each run uses a fresh invoice number.
Python workflow