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

# Beta quickstart

> Create a debtor and held invoice, upload a PDF, release the invoice, and report a payment.

<Warning>
  **Draft documentation — API not yet rolled out.** These documents are published in advance so you can prepare your integration. The contract and examples may change before release. Sandbox access will be provided separately; publication of these documents does not mean the API is available for testing or production use.
</Warning>

These examples have been checked against implementation source, but not yet tested end to end against a deployed beta. Use synthetic data once sandbox access is supplied.

## 1. Configure sandbox access

The shell examples require `curl` and `jq`. Replace the host placeholder with the sandbox API origin supplied by paywise and use your sandbox key. Keep keys out of source control and shared logs.

```bash theme={null}
export MAHNSERVICE_API_BASE="https://YOUR-SANDBOX-API-HOST/mahnservice/v1"
export MAHNSERVICE_API_KEY="YOUR-SANDBOX-KEY"

curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/info/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY"
```

Confirm the expected company and `environment: sandbox` before continuing. Configure the company's dunning flow in the portal before release; creating invoices can be tested first.

## 2. Create a debtor

```bash theme={null}
DEBTOR_RESPONSE=$(curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/debtors/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "Example Customer GmbH",
    "type": "company",
    "customer_number": "BETA-CUSTOMER-001",
    "email": "billing@example.com",
    "address": {
      "street": "Example Street 1",
      "zip_code": "10115",
      "city": "Berlin",
      "country": "DE"
    }
  }')
DEBTOR_ID=$(printf '%s' "$DEBTOR_RESPONSE" | jq -er '.id')
```

Persist `id` in your integration. **Do not blindly retry debtor creation:** it is not idempotent. A customer-number lookup is available, but does not guarantee uniqueness or prevent concurrent duplicate creation. See [retry rules](/api-docs/mahnservice-api/lifecycle#retries-and-duplicates).

## 3. Create a held invoice

Choose dates appropriate for your test: `document_date` must not be after `due_date`. Future-due invoices are accepted. Amounts must be positive.

```bash theme={null}
INVOICE_RESPONSE=$(curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY" \
  -H "Content-Type: application/json" \
  --data "$(jq -n --arg debtor "$DEBTOR_ID" '{
    invoice_number: "BETA-INVOICE-001",
    amount: "120.00",
    currency: "EUR",
    document_date: "2026-09-01",
    due_date: "2026-09-15",
    your_reference: "ERP-BETA-001",
    debtor: $debtor
  }')")
INVOICE_ID=$(printf '%s' "$INVOICE_RESPONSE" | jq -er '.id')
```

A new invoice returns `201` and `dunning_state: held`. An eligible existing invoice with the same number returns `200`; changed values in a repeated POST do not update it. Use `PATCH /invoices/{id}/` to correct a held invoice.

## 4. Upload an optional PDF and wait for readiness

This preview supports one PDF per invoice. Repeating the upload replaces it; multiple-PDF support will be documented in a later contract update.

Use a valid synthetic invoice PDF. Do not set the multipart Content-Type yourself: curl adds the required boundary.

```bash theme={null}
curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/documents/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY" \
  -F "file=@./sample-invoice.pdf;type=application/pdf"

curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY"
```

Read `document.status`: `pending`, `ready`, `failed`, or `rejected`. Poll with a delay until `ready`; stop and inspect failures rather than polling indefinitely. Uploading a supplied document is asynchronous: a successful upload does not mean it is usable yet. Release returns `409 document_not_ready` until it is ready. For a failed/rejected document, upload replacement bytes while the invoice remains held. There is no retry endpoint. A replacement removes the old PDF; a failed replacement does not restore it.

If you do not supply a document, this readiness gate does not apply.

## 5. Release explicitly

The release action takes **no request body**, including no empty JSON object.

```bash theme={null}
curl --fail-with-body --silent --show-error -X POST \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/release/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY"

curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/dunning/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY"
```

Release enrolls the invoice into the company default flow. Existing company/debtor pauses and activation state still apply; inspect the returned state rather than assuming immediate sending. The sandbox is a test environment, not a channel for live debtor contact.

## 6. Report a payment

This example reports full payment. Set `value_date` to the actual payment date; future payment dates are rejected. Use a stable reference identifying the payment.

```bash theme={null}
curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/payments/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "amount": "120.00",
    "value_date": "2026-09-23",
    "reference": "BETA-PAYMENT-001"
  }'

curl --fail-with-body --silent --show-error \
  "$MAHNSERVICE_API_BASE/invoices/$INVOICE_ID/" \
  -H "Authorization: Bearer $MAHNSERVICE_API_KEY"
```

Before Inkasso handover, covering the invoice amount marks it paid and stops dunning. After handover the collection case owns settlement. See the [lifecycle rules](/api-docs/mahnservice-api/lifecycle) for partial payments, retries, and terminal states.
