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

# Submit a rental order

> Create a rental agreement and H17 or H19 claims inline or in stages, then finalize and verify the order.

## Outcome

You have a finalized rental order with a complete rental agreement and at
least one valid commercial (H17) or residential (H19) rental claim. The order
uses the same submission, review, withdrawal, and acceptance lifecycle as an
invoice order.

## Prerequisites

* A production or sandbox base URL and Bearer key.
* An entitled company UUID on every call when using a Partner key.
* A debtor UUID with sufficient address and notification data.
* The rental agreement facts: property address, monthly Warmmiete, contract
  conclusion date, and — per unpaid month — the H17 or H19 claim.
* One stable `Idempotency-Key` per logical `POST` command.

## Lifecycle context

A rental order follows the same draft → `submitted` → `accepted` lifecycle
as an invoice order. The only variant-specific pieces are the singleton
`rental_agreement`, which the order owns from creation, and the H17/H19
claims whose dates the agreement's due rule derives. Documents are base64
inline uploads or later multipart uploads below the agreement or claim; they
scan asynchronously and never block finalization.

## 0. Create or reuse the debtor

Create the reusable debtor first. Each example parses the exact returned UUID;
persist it as `DEBTOR_ID` and use that value in the order request. Reconcile
an ambiguous retry by the exact `your_reference`; never guess an ID.

```python Python theme={null}
import uuid
import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"
DEBTOR_COMMAND_ID = "your-debtor-command-id"
DEBTOR_REFERENCE = "quickstart-debtor-80000000-0000-4000-8000-000000000001"
payload = {
    "your_reference": DEBTOR_REFERENCE,
    "acting_as": "consumer",
    "person": {"salutation": "mx", "first_name": "Alex", "last_name": "Example"},
    "addresses": [{"street": "Example Street 12", "postal_code": "10115", "city": "Berlin", "country": "DE", "primary": True}],
}
response = requests.post(
    f"{PAYWISE_API_URL}/v2/debtors/",
    headers={"Authorization": f"Bearer {PAYWISE_API_KEY}", "Content-Type": "application/json", "Idempotency-Key": DEBTOR_COMMAND_ID},
    json=payload,
    timeout=(5, 30),
)
if response.status_code != 201:
    response.raise_for_status()
    raise RuntimeError(f"Expected 201, received {response.status_code}")
debtor_id = str(uuid.UUID(response.json()["id"]))
```

```javascript JavaScript theme={null}
const payload = {
  your_reference: process.env.DEBTOR_REFERENCE,
  acting_as: "consumer",
  person: { salutation: "mx", first_name: "Alex", last_name: "Example" },
  addresses: [{ street: "Example Street 12", postal_code: "10115", city: "Berlin", country: "DE", primary: true }],
};
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/v2/debtors/`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": process.env.DEBTOR_COMMAND_ID },
  body: JSON.stringify(payload),
  signal: AbortSignal.timeout(30000),
});
if (response.status !== 201) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const debtorId = (await response.json()).id;
if (!/^[0-9a-f-]{36}$/i.test(debtorId)) throw new Error("Invalid debtor UUID");
```

```java Java theme={null}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

class CreateDebtorRequest {
  public static void main(String[] args) throws IOException, InterruptedException {
    String json = "{\"your_reference\":\"quickstart-debtor-80000000-0000-4000-8000-000000000001\",\"acting_as\":\"consumer\",\"person\":{\"salutation\":\"mx\",\"first_name\":\"Alex\",\"last_name\":\"Example\"},\"addresses\":[{\"street\":\"Example Street 12\",\"postal_code\":\"10115\",\"city\":\"Berlin\",\"country\":\"DE\",\"primary\":true}]}";
    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(System.getenv("PAYWISE_API_URL") + "/v2/debtors/"))
        .timeout(Duration.ofSeconds(30))
        .header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", System.getenv("DEBTOR_COMMAND_ID"))
        .POST(HttpRequest.BodyPublishers.ofString(json)).build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() != 201) throw new IOException(response.body());
    String debtorId = response.body().replaceFirst("(?s).*\\\"id\\\"\\s*:\\s*\\\"([0-9a-fA-F-]{36})\\\".*", "$1");
    if (debtorId.equals(response.body())) throw new IOException("Missing debtor UUID");
  }
}
```

```csharp C# theme={null}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

class CreateDebtorRequest {
    static async Task Main() {
        using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        var payload = new {
            your_reference = "quickstart-debtor-80000000-0000-4000-8000-000000000001",
            acting_as = "consumer",
            person = new { salutation = "mx", first_name = "Alex", last_name = "Example" },
            addresses = new[] { new { street = "Example Street 12", postal_code = "10115", city = "Berlin", country = "DE", primary = true } }
        };
        using var request = new HttpRequestMessage(HttpMethod.Post, $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/debtors/");
        request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
        request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("DEBTOR_COMMAND_ID"));
        request.Content = JsonContent.Create(payload);
        using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        using var response = await client.SendAsync(request, cancellation.Token);
        if ((int)response.StatusCode != 201) throw new HttpRequestException(await response.Content.ReadAsStringAsync());
        using var debtor = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        var debtorId = debtor.RootElement.GetProperty("id").GetGuid();
    }
}
```

```bash cURL theme={null}
status="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
  --request POST "$PAYWISE_API_URL/v2/debtors/" \
  --header "Authorization: Bearer $PAYWISE_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $DEBTOR_COMMAND_ID" \
  --output debtor-created.json --write-out '%{http_code}' \
  --data '{"your_reference":"quickstart-debtor-80000000-0000-4000-8000-000000000001","acting_as":"consumer","person":{"salutation":"mx","first_name":"Alex","last_name":"Example"},"addresses":[{"street":"Example Street 12","postal_code":"10115","city":"Berlin","country":"DE","primary":true}]}')"
[ "$status" -eq 201 ] || exit 1
DEBTOR_ID="$(jq -er '.id' debtor-created.json)" || exit 1
```

## 1. Create inline

Use one [POST `/v2/orders/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/create-order)
when the agreement, claim, and files are ready together. The nested operation
is atomic.

```python Python theme={null}
import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"
ORDER_COMMAND_ID = "your-order-command-id"
DEBTOR_ID = debtor_id  # Parsed from the immediately preceding create response.

payload = {
    "type": "rental",
    "debtor_id": DEBTOR_ID,
    "additional_debtor_ids": [],
    "starting_approach": "extrajudicial",
    "creditor_obligation_fulfilled": True,
    "rental_agreement": {
        "property_address": {
            "street": "Rental Example Street 8",
            "postal_code": "10115",
            "city": "Berlin",
            "country": "DE"
        },
        "monthly_rent": {
            "value": "1200.00",
            "currency": "EUR"
        },
        "contract_date": "2026-01-10",
        "documents": [
            {
                "type": "rental_agreement",
                "filename": "rental-agreement.pdf",
                "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
            }
        ]
    },
    "claims": [
        {
            "type": "receivable",
            "your_reference": "RENT-2026-02",
            "subject_matter": "Residential rent for February 2026",
            "document_date": "2026-02-01",
            "is_disputed": False,
            "principal_amount": {
                "value": "1200.00",
                "currency": "EUR"
            },
            "legal_basis": {
                "claim_type_code": "H19"
            },
            "documents": [
                {
                    "type": "claim_statement",
                    "filename": "rent-February-2026.pdf",
                    "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
                }
            ]
        }
    ]
}
response = requests.post(
    f"{PAYWISE_API_URL}/v2/orders/",
    headers={
        "Authorization": f"Bearer {PAYWISE_API_KEY}",
        "Content-Type": "application/json",
        "Idempotency-Key": ORDER_COMMAND_ID,
    },
    json=payload,
    timeout=(5, 30),
)
if response.status_code != 201:
    response.raise_for_status()
    raise RuntimeError(f"Expected 201, received {response.status_code}")
order = response.json()
order_id = order["id"]
matching_claims = [claim for claim in order.get("claims", []) if claim.get("your_reference") == "RENT-2026-02"]
if len(matching_claims) != 1:
    raise RuntimeError("Expected exactly one matching claim")
claim_id = matching_claims[0]["id"]
```

```javascript JavaScript theme={null}
const payload = {
  "type": "rental",
  "debtor_id": process.env.DEBTOR_ID,
    "additional_debtor_ids": [],
  "starting_approach": "extrajudicial",
  "creditor_obligation_fulfilled": true,
  "rental_agreement": {
    "property_address": {
      "street": "Rental Example Street 8",
      "postal_code": "10115",
      "city": "Berlin",
      "country": "DE"
    },
    "monthly_rent": {
      "value": "1200.00",
      "currency": "EUR"
    },
    "contract_date": "2026-01-10",
    "documents": [
      {
        "type": "rental_agreement",
        "filename": "rental-agreement.pdf",
        "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
      }
    ]
  },
  "claims": [
    {
      "type": "receivable",
      "your_reference": "RENT-2026-02",
      "subject_matter": "Residential rent for February 2026",
      "document_date": "2026-02-01",
      "is_disputed": false,
      "principal_amount": {
        "value": "1200.00",
        "currency": "EUR"
      },
      "legal_basis": {
        "claim_type_code": "H19"
      },
      "documents": [
        {
          "type": "claim_statement",
          "filename": "rent-February-2026.pdf",
          "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
        }
      ]
    }
  ]
};
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/v2/orders/`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": process.env.ORDER_COMMAND_ID,
  },
  body: JSON.stringify(payload),
  signal: AbortSignal.timeout(30000),
});
if (response.status !== 201) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const order = await response.json();
const orderId = order.id;
const matchingClaims = order.claims.filter((claim) => claim.your_reference === payload.claims[0].your_reference);
if (matchingClaims.length !== 1) throw new Error("Expected exactly one matching claim");
const claimId = matchingClaims[0].id;
```

```java Java theme={null}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

class RentalOrderRequest {
  static int closingDelimiter(String json, int opening, char open, char close) {
    int depth = 0;
    boolean inString = false;
    boolean escaped = false;
    for (int index = opening; index < json.length(); index++) {
      char character = json.charAt(index);
      if (inString) {
        if (escaped) escaped = false;
        else if (character == '\\') escaped = true;
        else if (character == '\"') inString = false;
      } else if (character == '\"') inString = true;
      else if (character == open) depth++;
      else if (character == close && --depth == 0) return index;
    }
    throw new IllegalArgumentException("Malformed JSON response");
  }
  static int closingQuote(String json, int opening) {
    boolean escaped = false;
    for (int index = opening + 1; index < json.length(); index++) {
      char character = json.charAt(index);
      if (escaped) escaped = false;
      else if (character == '\\') escaped = true;
      else if (character == '\"') return index;
    }
    throw new IllegalArgumentException("Malformed JSON string");
  }
  static int hexDigit(char character) {
    if (character >= '0' && character <= '9') return character - '0';
    if (character >= 'a' && character <= 'f') return character - 'a' + 10;
    if (character >= 'A' && character <= 'F') return character - 'A' + 10;
    throw new IllegalArgumentException("Malformed Unicode escape");
  }
  static int unicodeEscape(String json, int firstDigit, int closing) {
    if (firstDigit + 4 > closing) throw new IllegalArgumentException("Malformed Unicode escape");
    int value = 0;
    for (int index = firstDigit; index < firstDigit + 4; index++)
      value = value * 16 + hexDigit(json.charAt(index));
    return value;
  }
  static String decodeJsonString(String json, int opening, int closing) {
    StringBuilder decoded = new StringBuilder();
    for (int index = opening + 1; index < closing; index++) {
      char character = json.charAt(index);
      if (character == '\\') {
        if (++index >= closing) throw new IllegalArgumentException("Malformed JSON escape");
        char escape = json.charAt(index);
        switch (escape) {
          case '\"': decoded.append('\"'); break;
          case '\\': decoded.append('\\'); break;
          case '/': decoded.append('/'); break;
          case 'b': decoded.append('\b'); break;
          case 'f': decoded.append('\f'); break;
          case 'n': decoded.append('\n'); break;
          case 'r': decoded.append('\r'); break;
          case 't': decoded.append('\t'); break;
          case 'u':
            char unit = (char) unicodeEscape(json, index + 1, closing);
            index += 4;
            if (Character.isHighSurrogate(unit)) {
              if (index + 6 >= closing || json.charAt(index + 1) != '\\' || json.charAt(index + 2) != 'u')
                throw new IllegalArgumentException("Missing low surrogate");
              char low = (char) unicodeEscape(json, index + 3, closing);
              if (!Character.isLowSurrogate(low)) throw new IllegalArgumentException("Invalid low surrogate");
              decoded.appendCodePoint(Character.toCodePoint(unit, low));
              index += 6;
            } else if (Character.isLowSurrogate(unit)) throw new IllegalArgumentException("Unexpected low surrogate");
            else decoded.append(unit);
            break;
          default: throw new IllegalArgumentException("Unsupported JSON escape");
        }
      } else {
        if (character < 0x20) throw new IllegalArgumentException("Unescaped control character");
        if (Character.isHighSurrogate(character)) {
          if (index + 1 >= closing || !Character.isLowSurrogate(json.charAt(index + 1)))
            throw new IllegalArgumentException("Missing raw low surrogate");
          decoded.append(character).append(json.charAt(++index));
        } else if (Character.isLowSurrogate(character)) throw new IllegalArgumentException("Unexpected raw low surrogate");
        else decoded.append(character);
      }
    }
    return decoded.toString();
  }
  static String directStringField(String object, String wanted) {
    int opening = 0;
    while (opening < object.length() && Character.isWhitespace(object.charAt(opening))) opening++;
    if (opening >= object.length() || object.charAt(opening) != '{')
      throw new IllegalArgumentException("Expected JSON object");
    int closing = closingDelimiter(object, opening, '{', '}');
    for (int cursor = opening + 1; cursor < closing;) {
      while (cursor < closing && (Character.isWhitespace(object.charAt(cursor)) || object.charAt(cursor) == ',')) cursor++;
      if (cursor >= closing) break;
      if (object.charAt(cursor) != '\"') throw new IllegalArgumentException("Malformed JSON field");
      int keyClosing = closingQuote(object, cursor);
      String key = decodeJsonString(object, cursor, keyClosing);
      cursor = keyClosing + 1;
      while (cursor < closing && Character.isWhitespace(object.charAt(cursor))) cursor++;
      if (cursor >= closing || object.charAt(cursor++) != ':') throw new IllegalArgumentException("Malformed JSON field");
      while (cursor < closing && Character.isWhitespace(object.charAt(cursor))) cursor++;
      if (cursor >= closing) throw new IllegalArgumentException("Missing JSON value");
      char valueStart = object.charAt(cursor);
      if (valueStart == '\"') {
        int valueClosing = closingQuote(object, cursor);
        if (key.equals(wanted)) return decodeJsonString(object, cursor, valueClosing);
        cursor = valueClosing + 1;
      } else if (valueStart == '{' || valueStart == '[') {
        char valueClose = valueStart == '{' ? '}' : ']';
        if (key.equals(wanted)) throw new IllegalArgumentException("Expected string field " + wanted);
        cursor = closingDelimiter(object, cursor, valueStart, valueClose) + 1;
      } else {
        if (key.equals(wanted)) throw new IllegalArgumentException("Expected string field " + wanted);
        while (cursor < closing && object.charAt(cursor) != ',') cursor++;
      }
    }
    return null;
  }
  static String topLevelId(String object) {
    String id = object.replaceFirst(
        "(?s)^\\s*\\{(?:(?!\\{).)*?\\\"id\\\"\\s*:\\s*\\\"([^\\\"]+)\\\".*$", "$1");
    if (id.equals(object)) throw new IllegalArgumentException("Missing top-level id");
    return id;
  }
  static String[] responseIds(String body, String reference) {
    if (reference == null) throw new IllegalArgumentException("Missing business reference");
    String orderId = topLevelId(body);
    java.util.regex.Matcher claims = java.util.regex.Pattern
        .compile("\\\"claims\\\"\\s*:\\s*\\[").matcher(body);
    if (!claims.find()) throw new IllegalArgumentException("Missing claims collection");
    int opening = body.indexOf('[', claims.start());
    int closing = closingDelimiter(body, opening, '[', ']');
    int matches = 0;
    String claimId = null;
    for (int cursor = opening + 1; cursor < closing;) {
      while (cursor < closing && (Character.isWhitespace(body.charAt(cursor)) || body.charAt(cursor) == ',')) cursor++;
      if (cursor >= closing) break;
      if (body.charAt(cursor) != '{') throw new IllegalArgumentException("Malformed claim object");
      int claimClosing = closingDelimiter(body, cursor, '{', '}');
      String claim = body.substring(cursor, claimClosing + 1);
      if (reference.equals(directStringField(claim, "your_reference"))) {
        matches++;
        claimId = directStringField(claim, "id");
        if (claimId == null) throw new IllegalArgumentException("Missing direct claim id");
      }
      cursor = claimClosing + 1;
    }
    if (matches != 1) throw new IllegalArgumentException("Expected exactly one matching claim");
    return new String[] {orderId, claimId};
  }
  public static void main(String[] args) throws IOException, InterruptedException {
    String json = "{\"type\":\"rental\",\"debtor_id\":\"" + System.getenv("DEBTOR_ID") + "\",\"additional_debtor_ids\":[],\"starti"
        + "ng_approach\":\"extrajudicial\",\"creditor_obligation_fulfilled\":true,\"renta"
        + "l_agreement\":{\"property_address\":{\"street\":\"Rental Example Street 8\",\"po"
        + "stal_code\":\"10115\",\"city\":\"Berlin\",\"country\":\"DE\"},\"monthly_rent\":{\"valu"
        + "e\":\"1200.00\",\"currency\":\"EUR\"},\"contract_date\":\"2026-01-10\",\"documents\":"
        + "[{\"type\":\"rental_agreement\",\"filename\":\"rental-agreement.pdf\",\"base64\":\""
        + "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5k"
        + "b2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4K"
        + "ZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3gg"
        + "WzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAow"
        + "MDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAw"
        + "MCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4Ngol"
        + "JUVPRgo=\"}]},\"claims\":[{\"type\":\"receivable\",\"your_reference\":\"RENT-2026-"
        + "02\",\"subject_matter\":\"Residential rent for February 2026\",\"document_date"
        + "\":\"2026-02-01\",\"is_disputed\":false,\"principal_amount\":{\"value\":\"1200.00\""
        + ",\"currency\":\"EUR\"},\"legal_basis\":{\"claim_type_code\":\"H19\"},\"documents\":["
        + "{\"type\":\"claim_statement\",\"filename\":\"rent-February-2026.pdf\",\"base64\":\""
        + "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5k"
        + "b2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4K"
        + "ZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3gg"
        + "WzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAow"
        + "MDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAw"
        + "MCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4Ngol"
        + "JUVPRgo=\"}]}]}";
    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(System.getenv("PAYWISE_API_URL") + "/v2/orders/"))
        .timeout(Duration.ofSeconds(30))
        .header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", System.getenv("ORDER_COMMAND_ID"))
        .POST(HttpRequest.BodyPublishers.ofString(json)).build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() != 201) throw new IOException(response.body());
    String[] ids = responseIds(response.body(), "RENT-2026-02");
    String orderId = ids[0];
    String claimId = ids[1];
  }
}
```

```csharp C# theme={null}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

class RentalOrderRequest
{
    static async Task Main()
    {
        using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        var payload = new {
            type = "rental",
            debtor_id = Environment.GetEnvironmentVariable("DEBTOR_ID"), additional_debtor_ids = Array.Empty<string>(),
            starting_approach = "extrajudicial",
            creditor_obligation_fulfilled = true,
            rental_agreement = new {
                property_address = new {
                    street = "Rental Example Street 8",
                    postal_code = "10115",
                    city = "Berlin",
                    country = "DE"
                },
                monthly_rent = new {
                    value = "1200.00",
                    currency = "EUR"
                },
                contract_date = "2026-01-10",
                documents = new[] {
                    new {
                        type = "rental_agreement",
                        filename = "rental-agreement.pdf",
                        base64 = "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
                    }
                }
            },
            claims = new[] {
                new {
                    type = "receivable",
                    your_reference = "RENT-2026-02",
                    subject_matter = "Residential rent for February 2026",
                    document_date = "2026-02-01",
                    is_disputed = false,
                    principal_amount = new {
                        value = "1200.00",
                        currency = "EUR"
                    },
                    legal_basis = new {
                        claim_type_code = "H19"
                    },
                    documents = new[] {
                        new {
                            type = "claim_statement",
                            filename = "rent-February-2026.pdf",
                            base64 = "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
                        }
                    }
                }
            }
        };
        using var request = new HttpRequestMessage(HttpMethod.Post,
            $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/orders/");
        request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
        request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("ORDER_COMMAND_ID"));
        request.Content = JsonContent.Create(payload);
        using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        using var response = await client.SendAsync(request, cancellation.Token);
        if ((int)response.StatusCode != 201) throw new HttpRequestException(await response.Content.ReadAsStringAsync());
        using var order = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        var orderId = order.RootElement.GetProperty("id").GetGuid();
        var matches = order.RootElement.GetProperty("claims").EnumerateArray()
            .Where(claim => claim.GetProperty("your_reference").GetString() == payload.claims[0].your_reference).ToArray();
        if (matches.Length != 1) throw new InvalidOperationException("Expected exactly one matching claim");
        var claimId = matches[0].GetProperty("id").GetGuid();
    }
}
```

```bash cURL theme={null}
http_status="$(curl --fail-with-body --output order-response.json --write-out '%{http_code}' --silent --show-error --connect-timeout 5 --max-time 30 \
  --request POST "$PAYWISE_API_URL/v2/orders/" \
  --header "Authorization: Bearer $PAYWISE_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $ORDER_COMMAND_ID" \
  --data @- <<JSON
{
  "type": "rental",
  "debtor_id": "$DEBTOR_ID",
    "additional_debtor_ids": [],
  "starting_approach": "extrajudicial",
  "creditor_obligation_fulfilled": true,
  "rental_agreement": {
    "property_address": {
      "street": "Rental Example Street 8",
      "postal_code": "10115",
      "city": "Berlin",
      "country": "DE"
    },
    "monthly_rent": {
      "value": "1200.00",
      "currency": "EUR"
    },
    "contract_date": "2026-01-10",
    "documents": [
      {
        "type": "rental_agreement",
        "filename": "rental-agreement.pdf",
        "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
      }
    ]
  },
  "claims": [
    {
      "type": "receivable",
      "your_reference": "RENT-2026-02",
      "subject_matter": "Residential rent for February 2026",
      "document_date": "2026-02-01",
      "is_disputed": false,
      "principal_amount": {
        "value": "1200.00",
        "currency": "EUR"
      },
      "legal_basis": {
        "claim_type_code": "H19"
      },
      "documents": [
        {
          "type": "claim_statement",
          "filename": "rent-February-2026.pdf",
          "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="
        }
      ]
    }
  ]
}
JSON
)"
[ "$http_status" -eq 201 ] || exit 1
ORDER_ID="$(jq -er '.id' order-response.json)" || exit 1
CLAIM_ID="$(jq -er --arg ref "RENT-2026-02" '[.claims[] | select(.your_reference == $ref)] | if length == 1 then .[0].id else error("expected exactly one matching claim") end' order-response.json)" || exit 1
```

`contract_date` is the date the rental contract was concluded. It is not the
move-in date or the beginning of the tenancy. `monthly_rent` is the recurring
Warmmiete including ancillary-cost prepayments; it provides agreement context
and does not cap or determine an individual claim amount.

## 2. Let dates derive—or override them deliberately

When `rental_agreement.due_rule` is omitted or `null`, both H17 and H19 claims
default to the third German business day of the rental month. German business
days are Monday through Friday excluding German federal holidays.

For each H17/H19 claim:

* `document_date` must be the first day of the claimed month.
* Omitted `legal_basis.contract_date` is copied from the agreement. A different
  supplied value returns `400`.
* Omitted `due_date` is derived from the agreement rule.
* Omitted `delay_date` becomes the day after the resolved due date.
* An explicit `delay_date` must be later than the resolved `due_date`.
* Explicit due and delay dates remain claim-level overrides. Patching either
  field to `null` restores server derivation.
* A later agreement due-rule change recomputes derived dates and preserves
  explicit overrides — with one exception: an explicit `delay_date` that
  falls on or before the recomputed `due_date` is reset to `due_date + 1`
  and stops being an override. Reminders are informational and do not change
  this rule.
* A rental-agreement `contract_date` (set inline or through a later
  agreement `PATCH`) is written to every rental claim's
  `legal_basis.contract_date`, so an order whose contract date arrives
  through the agreement satisfies readiness.
* Every date is `YYYY-MM-DD` in ASCII digits; `9999-12-30` is the latest
  accepted `due_date` (`delay_date` is its successor) and the latest accepted
  reminder `date`/`due_date`; a later value is `400` on that field with code
  `invalid`.

One order cannot mix H17 and H19 or contain two rental claims for the same
month. A mixed family returns `400`; a duplicate month returns `409`, so patch
the existing claim instead. Other `ReceivableClaim` entries may coexist with the
rental claims.

## 3. Build the draft in stages

Create the empty draft first, then use a distinct stable idempotency key for
this logical claim command. Preserve the exact key and body for replay, and
store the returned claim UUID after verifying its exact business reference.

```python Python theme={null}
import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"
CREATE_CLAIM_KEY = "your-distinct-create-claim-key"
claim_payload = {
    "order_id": "20000000-0000-4000-8000-000000000001",
    "type": "receivable",
    "your_reference": "RENT-2026-02",
    "subject_matter": "Residential rent for February 2026",
    "document_date": "2026-02-01",
    "is_disputed": False,
    "principal_amount": {"value": "1200.00", "currency": "EUR"},
    "legal_basis": {"claim_type_code": "H19"},
    "documents": [{"type": "claim_statement", "filename": "rent-February-2026.pdf", "base64": "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="}],
}
response = requests.post(
    f"{PAYWISE_API_URL}/v2/claims/",
    headers={"Authorization": f"Bearer {PAYWISE_API_KEY}", "Content-Type": "application/json", "Idempotency-Key": CREATE_CLAIM_KEY},
    json=claim_payload,
    timeout=(5, 30),
)
if response.status_code != 201:
    response.raise_for_status()
    raise RuntimeError(f"Expected 201, received {response.status_code}")
claim = response.json()
if claim.get("your_reference") != claim_payload["your_reference"]:
    raise RuntimeError("Claim business reference mismatch")
claim_id = claim["id"]
```

```javascript JavaScript theme={null}
const claimPayload = {
  order_id: "20000000-0000-4000-8000-000000000001",
  type: "receivable",
  your_reference: "RENT-2026-02",
  subject_matter: "Residential rent for February 2026",
  document_date: "2026-02-01",
  is_disputed: false,
  principal_amount: { value: "1200.00", currency: "EUR" },
  legal_basis: { claim_type_code: "H19" },
  documents: [{ type: "claim_statement", filename: "rent-February-2026.pdf", base64: "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo=" }],
};
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/v2/claims/`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": process.env.CREATE_CLAIM_KEY },
  body: JSON.stringify(claimPayload),
  signal: AbortSignal.timeout(30000),
});
if (response.status !== 201) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const claim = await response.json();
if (claim.your_reference !== claimPayload.your_reference) throw new Error("Claim business reference mismatch");
const claimId = claim.id;
```

```java Java theme={null}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

class CreateClaimRequest {
  static String quoteJsonString(String value) {
    if (value == null) throw new IllegalArgumentException("Missing business reference");
    return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
  }
  static String topLevelId(String object) {
    String id = object.replaceFirst(
        "(?s)^\\s*\\{(?:(?!\\{).)*?\\\"id\\\"\\s*:\\s*\\\"([^\\\"]+)\\\".*$", "$1");
    if (id.equals(object)) throw new IllegalArgumentException("Missing top-level claim id");
    return id;
  }
  static String createdClaimId(String body, String reference) {
    String referencePattern = "(?s)^\\s*\\{(?:(?!\\{).)*?\\\"your_reference\\\"\\s*:\\s*"
        + java.util.regex.Pattern.quote(quoteJsonString(reference));
    if (!java.util.regex.Pattern.compile(referencePattern).matcher(body).find())
      throw new IllegalArgumentException("Claim business reference mismatch");
    return topLevelId(body);
  }
  public static void main(String[] args) throws IOException, InterruptedException {
    String json = "{\"order_id\":\"20000000-0000-4000-8000-000000000001\",\"type\":\"receivable\",\"your_reference\":\"RENT-2026-02\",\"subject_matter\":\"Residential rent for February 2026\",\"document_date\":\"2026-02-01\",\"is_disputed\":false,\"principal_amount\":{\"value\":\"1200.00\",\"currency\":\"EUR\"},\"legal_basis\":{\"claim_type_code\":\"H19\"},\"documents\":[{\"type\":\"claim_statement\",\"filename\":\"rent-February-2026.pdf\",\"base64\":\"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo=\"}]}";
    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(System.getenv("PAYWISE_API_URL") + "/v2/claims/"))
        .timeout(Duration.ofSeconds(30))
        .header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", System.getenv("CREATE_CLAIM_KEY"))
        .POST(HttpRequest.BodyPublishers.ofString(json)).build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() != 201) throw new IOException(response.body());
    String claimId = createdClaimId(response.body(), "RENT-2026-02");
  }
}
```

```csharp C# theme={null}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

class CreateClaimRequest {
    static async Task Main() {
        using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        var payload = new {
            order_id = "20000000-0000-4000-8000-000000000001", type = "receivable",
            your_reference = "RENT-2026-02",
            subject_matter = "Residential rent for February 2026",
            document_date = "2026-02-01",
            is_disputed = false,
            principal_amount = new { value = "1200.00", currency = "EUR" },
            legal_basis = new { claim_type_code = "H19" },
            documents = new[] { new { type = "claim_statement", filename = "rent-February-2026.pdf", base64 = "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo=" } }
        };
        using var request = new HttpRequestMessage(HttpMethod.Post, $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/claims/");
        request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
        request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("CREATE_CLAIM_KEY"));
        request.Content = JsonContent.Create(payload);
        using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        using var response = await client.SendAsync(request, cancellation.Token);
        if ((int)response.StatusCode != 201) throw new HttpRequestException(await response.Content.ReadAsStringAsync());
        using var claim = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        if (claim.RootElement.GetProperty("your_reference").GetString() != payload.your_reference) throw new InvalidOperationException("Claim business reference mismatch");
        var claimId = claim.RootElement.GetProperty("id").GetGuid();
    }
}
```

```bash cURL theme={null}
status="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
  --request POST "$PAYWISE_API_URL/v2/claims/" \
  --header "Authorization: Bearer $PAYWISE_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $CREATE_CLAIM_KEY" \
  --output claim-created.json --write-out '%{http_code}' \
  --data '{"order_id":"20000000-0000-4000-8000-000000000001","type":"receivable","your_reference":"RENT-2026-02","subject_matter":"Residential rent for February 2026","document_date":"2026-02-01","is_disputed":false,"principal_amount":{"value":"1200.00","currency":"EUR"},"legal_basis":{"claim_type_code":"H19"},"documents":[{"type":"claim_statement","filename":"rent-February-2026.pdf","base64":"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAyMDAgMTAwXSA+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjE4NgolJUVPRgo="}]}')"
[ "$status" -eq 201 ] || exit 1
CLAIM_ID="$(jq -er --arg ref "RENT-2026-02" 'if .your_reference == $ref then .id else error("claim business reference mismatch") end' claim-created.json)" || exit 1
```

For a commercial tenancy, create the same empty rental draft, update its
agreement as needed, and send a complete H17 claim to the same collection.
For example, the claim body still includes the draft UUID:

```json theme={null}
{
  "order_id": "20000000-0000-4000-8000-000000000001",
  "type": "receivable",
  "your_reference": "COMMERCIAL-RENT-2026-03",
  "subject_matter": "Commercial rent for March 2026",
  "document_date": "2026-03-01",
  "due_date": "2026-03-10",
  "delay_date": "2026-03-11",
  "is_disputed": false,
  "principal_amount": {"value": "2400.00", "currency": "EUR"},
  "legal_basis": {"claim_type_code": "H17"}
}
```

Keep one primary `rental_agreement` document on the agreement and one relevant
`claim_statement` on each rental claim. A second primary of the same type
conflicts with `409`; delete the old draft document before replacing it.
Agreement documents are optional at finalization, but every claim still needs
a non-empty `subject_matter` or a supporting document.

## 4. Finalize or remove the draft

Finalize with an empty-body
[POST `/v2/orders/{id}/finalize/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/finalize-order)
and a fresh `Idempotency-Key`. A rental draft is incomplete until it has a
complete property address, positive monthly rent, contract date, and at least
one H17/H19 claim. Missing readiness data returns `422` with public paths such
as `rental_agreement.contract_date`; invalid commands return `400`, and
conflicts return `409`.

Read the order after any ambiguous result. Once submitted, it may be withdrawn
while still eligible. If the draft should never be submitted, use
[DELETE `/v2/orders/{id}/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/delete-draft-order)
instead; draft deletion also removes its rental agreement.

## Runnable Python workflow

This orchestration proves the sandbox before any write, builds the rental
order in stages (empty draft → agreement → claim), uses one generated key per
logical POST, and never automatically repeats an ambiguous finalize call.

```python Python workflow theme={null}
import uuid
import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"
DEBTOR_ID = "40000000-0000-4000-8000-000000000001"

base_url = PAYWISE_API_URL
if not base_url.startswith("https://"):
    raise ValueError("PAYWISE_API_URL must be an HTTPS sandbox URL")
headers = {"Authorization": f"Bearer {PAYWISE_API_KEY}"}

probe = requests.get(
    f"{base_url}/v2/orders/",
    headers=headers,
    params={"limit": 1},
    timeout=(5, 30),
)
if probe.status_code != 200:
    probe.raise_for_status()
    raise RuntimeError(f"Expected 200, received {probe.status_code}")
if probe.headers.get("X-Paywise-Environment", "").lower() != "sandbox":
    raise RuntimeError("Refusing writes without authenticated sandbox proof")

# Stage 1: an empty rental draft. The order owns its rental agreement from
# creation, so the singleton can be completed in a separate step.
create_key = str(uuid.uuid4())
created = requests.post(
    f"{base_url}/v2/orders/",
    headers={**headers, "Idempotency-Key": create_key},
    json={
        "type": "rental",
        "debtor_id": DEBTOR_ID,
        "starting_approach": "extrajudicial",
        "creditor_obligation_fulfilled": True,
        "claims": [],
    },
    timeout=(5, 30),
)
if created.status_code != 201:
    created.raise_for_status()
    raise RuntimeError(f"Expected 201, received {created.status_code}")
order = created.json()
if order.get("type") != "rental" or order.get("status") != "draft":
    raise RuntimeError("Created order is not an editable rental draft")
order_id = order["id"]

# Stage 2: complete the agreement; omitted due_rule keeps the default
# third-business-day derivation for every rental claim.
agreement = requests.patch(
    f"{base_url}/v2/orders/{order_id}/rental-agreement/",
    headers=headers,
    json={
        "property_address": {
            "street": "Rental Example Street 8",
            "postal_code": "10115",
            "city": "Berlin",
            "country": "DE",
        },
        "monthly_rent": {"value": "1200.00", "currency": "EUR"},
        "contract_date": "2026-01-10",
    },
    timeout=(5, 30),
)
if agreement.status_code != 200:
    agreement.raise_for_status()
    raise RuntimeError(f"Expected 200, received {agreement.status_code}")

# Stage 3: one residential rental month; due and delay dates derive from
# the agreement, contract_date is copied from it.
claim_key = str(uuid.uuid4())
claim_created = requests.post(
    f"{base_url}/v2/claims/",
    headers={**headers, "Idempotency-Key": claim_key},
    json={
        "order_id": order_id,
        "type": "receivable",
        "your_reference": f"RENT-2026-02-{order_id}",
        "subject_matter": "Residential rent for February 2026",
        "document_date": "2026-02-01",
        "is_disputed": False,
        "principal_amount": {"value": "1200.00", "currency": "EUR"},
        "legal_basis": {"claim_type_code": "H19"},
    },
    timeout=(5, 30),
)
if claim_created.status_code != 201:
    claim_created.raise_for_status()
    raise RuntimeError(f"Expected 201, received {claim_created.status_code}")
claim = claim_created.json()
expected_reference = f"RENT-2026-02-{order_id}"
if claim.get("your_reference") != expected_reference:
    raise RuntimeError("Claim business reference mismatch")
claim_id = claim["id"]

finalize_key = str(uuid.uuid4())
try:
    finalized = requests.post(
        f"{base_url}/v2/orders/{order_id}/finalize/",
        headers={**headers, "Idempotency-Key": finalize_key},
        timeout=(5, 30),
    )
except requests.Timeout:
    recovered = requests.get(
        f"{base_url}/v2/orders/{order_id}/",
        headers=headers,
        timeout=(5, 30),
    )
    if recovered.status_code != 200:
        recovered.raise_for_status()
        raise RuntimeError(f"Expected 200, received {recovered.status_code}")
    order = recovered.json()
    if order.get("status") not in {
        "submitted",
        "awaiting_client_response",
        "accepted",
        "rejected",
        "withdrawn",
        "expired",
    }:
        raise RuntimeError(
            "Finalize outcome remains ambiguous; inspect the draft before deciding whether to retry with the same key"
        )
else:
    if finalized.status_code != 200:
        finalized.raise_for_status()
        raise RuntimeError(f"Expected 200, received {finalized.status_code}")
    order = finalized.json()

WORKFLOW_RESULT = {"order_id": order_id, "claim_id": claim_id, "status": order["status"]}
```

## Failure and recovery

* `400 validation_error` on create or claim commands: a mixed H17/H19
  family, a claim `document_date` that is not the first of the month, a
  `legal_basis.contract_date` that differs from the agreement, or a
  `delay_date` not later than the resolved `due_date`. Correct the field
  paths named in `errors[]` and issue a new logical command.
* `409` on a claim create: a rental claim for that month already exists —
  patch the existing claim instead of retrying. `409` on a document create:
  a primary `rental_agreement` or `claim_statement` already exists; delete
  the old draft document first.
* `422` on finalize: the readiness paths (for example
  `rental_agreement.contract_date`, `rental_agreement.property_address`,
  or `claims.<id>.subject_matter`) name what the draft still lacks. A
  claim whose stored `delay_date` is not after its `due_date` is reported
  as `claims.<id>.delay_date` with code `delay_before_due`
  (`delay_date must be after due_date.`). The draft stays editable;
  complete it and finalize with a fresh key.
* Finalize timeout, `500`, or `503`: read the order by ID before deciding
  on a retry. Only an observed `draft` justifies repeating finalize with the
  **same** key.
* `429`: wait at least the integer seconds in `Retry-After`, then retry the
  same command and key.

## Verify

```python Python theme={null}
import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"
ORDER_ID = "20000000-0000-4000-8000-000000000001"

response = requests.get(
    f"{PAYWISE_API_URL}/v2/orders/{ORDER_ID}/",
    headers={"Authorization": f"Bearer {PAYWISE_API_KEY}"},
    timeout=(5, 30),
)
if response.status_code != 200:
    response.raise_for_status()
    raise RuntimeError(f"Expected 200, received {response.status_code}")
```

```javascript JavaScript theme={null}
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/v2/orders/${process.env.ORDER_ID}/`, {
  method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
```

```java Java theme={null}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

class ReadRentalOrderRequest {
  public static void main(String[] args) throws IOException, InterruptedException {
    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
    HttpRequest request = HttpRequest.newBuilder().uri(URI.create(System.getenv("PAYWISE_API_URL") + "/v2/orders/" + System.getenv("ORDER_ID") + "/"))
        .timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY")).GET().build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() != 200) throw new IOException(response.body());
  }
}
```

```csharp C# theme={null}
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class ReadRentalOrderRequest
{
    static async Task Main()
    {
        using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        using var request = new HttpRequestMessage(HttpMethod.Get, $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/orders/{Environment.GetEnvironmentVariable("ORDER_ID")}/");
        request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
        using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        using var response = await client.SendAsync(request, cancellation.Token);
        if ((int)response.StatusCode != 200) throw new HttpRequestException(await response.Content.ReadAsStringAsync());
    }
}
```

```bash cURL theme={null}
http_status="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
  --request GET "$PAYWISE_API_URL/v2/orders/$ORDER_ID/" \
  --header "Authorization: Bearer $PAYWISE_API_KEY" \
  --output rental-order.json --write-out '%{http_code}')"
[ "$http_status" -eq 200 ] || exit 1
```

Require `.type` to be `rental`, `.status` to be `submitted` or `accepted`,
and `.rental_agreement.property_address` plus at least one claim with
`legal_basis.claim_type_code` `H17` or `H19` before recording the workflow
as successful.

## Reconcile acceptance by claim UUID

Webhook deliveries are at least once. On `order.accepted`, intersect the event's
`claim_ids` with the claim UUIDs stored by exact business reference. For every
match, refetch `/v2/claims/{claim_id}/` and trust the claim's current
`order_id` and `mandate_id`, even when the event order differs from the
originally submitted order. The claim UUID remains stable; do not scan orders
or reconstruct merge chains.

## Related reference

* [GET `/v2/orders/{order_id}/rental-agreement/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/get-rental-agreement)
* [PATCH `/v2/orders/{order_id}/rental-agreement/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/update-rental-agreement)
* [POST `/v2/orders/{order_id}/rental-agreement/documents/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/create-rental-agreement-document)
* [GET `/v2/orders/{id}/`](https://docs.paywise.de/api-docs/case-management-api/reference/orders/get-order)


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