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

# Case Management quickstart

> Create a debtor and invoice claim, finalize without a body, and find the accepted mandate.

This quickstart is designed for the sandbox and uses a direct Case 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.
A Partner key must add `X-On-Behalf-Of-Company: <company-uuid>` to every Case
request instead.

Use the documented trailing slash on `/v2/` roots and resource routes.
Slashless requests such as `/v2/info` return JSON `404` for every method,
including `GET`, `HEAD`, `OPTIONS`, and writes, with no redirect or `Location`
header. Request `/v2/info/` directly. See
[canonical URLs](/api-docs/essentials/requests-and-responses#canonical-urls).

The request tabs below are complete, copyable examples for individual
operations. The final section runs the whole flow end to end once in Python.

This shortest path uses the default `invoice` order type. For subtype-specific
data and validation, follow [Submit a rental order](/api-docs/case-management-api/workflows/submit-a-rental-order)
or [Submit a titled order](/api-docs/case-management-api/workflows/submit-a-titled-order).

## 1. Create the debtor record

Use a fresh `DEBTOR_REFERENCE` and `DEBTOR_COMMAND_ID` for a new logical write.
Keep the same body and idempotency key when retrying an ambiguous result.

<CodeGroup>
  ```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"

  debtor_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=debtor_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 debtorPayload = {
    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(debtorPayload),
    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 QuickstartRequest {
    static String jsonString(String value) {
      if (value == null) throw new IllegalArgumentException("Missing environment value");
      StringBuilder escaped = new StringBuilder("\"");
      for (int index = 0; index < value.length(); index++) {
        char character = value.charAt(index);
        switch (character) {
          case '\"': escaped.append("\\\""); break;
          case '\\': escaped.append("\\\\"); break;
          case '\b': escaped.append("\\b"); break;
          case '\f': escaped.append("\\f"); break;
          case '\n': escaped.append("\\n"); break;
          case '\r': escaped.append("\\r"); break;
          case '\t': escaped.append("\\t"); break;
          default:
            if (character < 0x20) escaped.append(String.format("\\u%04x", (int) character));
            else escaped.append(character);
        }
      }
      return escaped.append('\"').toString();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
      String json = "{\"your_reference\":" + jsonString(System.getenv("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}]}";
      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 QuickstartRequest
  {
      static async Task Main()
      {
          using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
          var payload = new {
              your_reference = Environment.GetEnvironmentVariable("DEBTOR_REFERENCE"),
              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}
  http_status="$(curl --fail-with-body --output quickstart-response.json --write-out '%{http_code}' --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" \
    --data @- <<JSON
  {
    "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
    }]
  }
  JSON
  )"
  [ "$http_status" -eq 201 ] || exit 1
  DEBTOR_ID="$(jq -er '.id' quickstart-response.json)" || exit 1
  ```
</CodeGroup>

The create response returns the debtor's `id` directly. Every language tab
above parses and retains that exact UUID; reuse it as `DEBTOR_ID` in step 2:

```json theme={null}
{"id": "acaf6cc1-f711-42c9-a385-7a3f537325cc", "created_at": "…", …}
```

If you lose the id, `GET /v2/debtors/?your_reference=<reference>` finds the
debtor again. The reference is not enforced unique and the filter is a
case-insensitive substring match, so compare `your_reference` exactly on the
results and treat more than one match as a reconciliation task.

## 2. Create the order draft

This is a `ReceivableClaim` in an invoice order, so the claim declares
`"type": "receivable"` (required on every claim). Omitting the order-level
`type` defaults the order to `invoice`; no rental-agreement or
enforceable-title fields belong in this payload. Choose `legal_basis.claim_type_code`
from the complete [German code catalog](/api-docs/case-management-api/concepts/claims#legal-basis-codes);
this example uses H05 for a service contract.

<CodeGroup>
  ```python Python theme={null}
  import requests

  PAYWISE_API_URL = "https://api-sandbox.paywise.de"
  PAYWISE_API_KEY = "pw_sbx_your_api_key"
  CLAIM_REFERENCE = "quickstart-claim-80000000-0000-4000-8000-000000000001"
  DEBTOR_ID = "40000000-0000-4000-8000-000000000001"
  DOCUMENT_REFERENCE = "INV-80000000-0000-4000-8000-000000000001"
  ORDER_COMMAND_ID = "your-order-command-id"

  order_payload = {
      "debtor_id": DEBTOR_ID,
      "additional_debtor_ids": [],
      "starting_approach": "extrajudicial",
      "creditor_obligation_fulfilled": True,
      "claims": [{
          "type": "receivable",
          "your_reference": CLAIM_REFERENCE,
          "document_reference": DOCUMENT_REFERENCE,
          "subject_matter": "Consulting services for June 2026",
          "principal_amount": {"value": "125.50", "currency": "EUR"},
          "document_date": "2026-06-30", "due_date": "2026-07-14",
          "delay_date": "2026-07-20",
          "is_disputed": False, "items": [], "additional_charges": [],
          "reminders": [], "documents": [],
          "legal_basis": {"claim_type_code": "H05", "contract_date": "2026-06-01", "description": "Consulting agreement"},
      }],
  }
  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=order_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 = str(uuid.UUID(order["id"]))
  matching_claims = [
      claim for claim in order.get("claims", [])
      if claim.get("your_reference") == CLAIM_REFERENCE
  ]
  if len(matching_claims) != 1:
      raise RuntimeError("Expected exactly one claim with the submitted business reference")
  claim_id = str(uuid.UUID(matching_claims[0]["id"]))
  ```

  ```javascript JavaScript theme={null}
  const orderPayload = {
    debtor_id: process.env.DEBTOR_ID,
    additional_debtor_ids: [], starting_approach: "extrajudicial",
    creditor_obligation_fulfilled: true,
    claims: [{
      type: "receivable",
      your_reference: process.env.CLAIM_REFERENCE,
      document_reference: process.env.DOCUMENT_REFERENCE,
      subject_matter: "Consulting services for June 2026",
      principal_amount: { value: "125.50", currency: "EUR" },
      document_date: "2026-06-30", due_date: "2026-07-14", delay_date: "2026-07-20",
      is_disputed: false,
      items: [], additional_charges: [], reminders: [], documents: [],
      legal_basis: { claim_type_code: "H05", contract_date: "2026-06-01", description: "Consulting agreement" },
    }],
  };
  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(orderPayload),
    signal: AbortSignal.timeout(30000),
  });
  if (response.status !== 201) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  const order = await response.json();
  const matchingClaims = order.claims.filter((claim) => claim.your_reference === process.env.CLAIM_REFERENCE);
  if (matchingClaims.length !== 1) throw new Error("Expected exactly one claim with the submitted business reference");
  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 QuickstartRequest {
    static String jsonString(String value) {
      if (value == null) throw new IllegalArgumentException("Missing environment value");
      StringBuilder escaped = new StringBuilder("\"");
      for (int index = 0; index < value.length(); index++) {
        char character = value.charAt(index);
        switch (character) {
          case '\"': escaped.append("\\\""); break;
          case '\\': escaped.append("\\\\"); break;
          case '\b': escaped.append("\\b"); break;
          case '\f': escaped.append("\\f"); break;
          case '\n': escaped.append("\\n"); break;
          case '\r': escaped.append("\\r"); break;
          case '\t': escaped.append("\\t"); break;
          default:
            if (character < 0x20) escaped.append(String.format("\\u%04x", (int) character));
            else escaped.append(character);
        }
      }
      return escaped.append('\"').toString();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
      String json = "{\"debtor_id\":" + jsonString(System.getenv("DEBTOR_ID"))
          + ",\"additional_debtor_ids\":[],\"starting_approach\":\"extrajudicial\","
          + "\"creditor_obligation_fulfilled\":true,\"claims\":[{\"type\":\"receivable\",\"your_reference\":"
          + jsonString(System.getenv("CLAIM_REFERENCE")) + ",\"document_reference\":"
          + jsonString(System.getenv("DOCUMENT_REFERENCE")) + ",\"subject_matter\":\"Consulting services for June 2026\","
          + "\"principal_amount\":{\"value\":\"125.50\",\"currency\":\"EUR\"},"
          + "\"document_date\":\"2026-06-30\",\"due_date\":\"2026-07-14\","
          + "\"delay_date\":\"2026-07-20\",\"is_disputed\":false,"
          + "\"items\":[],\"additional_charges\":[],\"reminders\":[],\"documents\":[],"
          + "\"legal_basis\":{\"claim_type_code\":\"H05\",\"contract_date\":\"2026-06-01\","
          + "\"description\":\"Consulting agreement\"}}]}";
      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());
      // Parse the JSON response with your JSON library and require exactly one
      // claims[] entry whose your_reference equals CLAIM_REFERENCE; persist its id.
    }
  }
  ```

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

  class QuickstartRequest
  {
      static async Task Main()
      {
          using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
          var payload = new {
              debtor_id = Environment.GetEnvironmentVariable("DEBTOR_ID"),
              additional_debtor_ids = Array.Empty<string>(), starting_approach = "extrajudicial",
              creditor_obligation_fulfilled = true,
              claims = new[] { new {
                  type = "receivable",
                  your_reference = Environment.GetEnvironmentVariable("CLAIM_REFERENCE"),
                  document_reference = Environment.GetEnvironmentVariable("DOCUMENT_REFERENCE"),
                  subject_matter = "Consulting services for June 2026",
                  principal_amount = new { value = "125.50", currency = "EUR" },
                  document_date = "2026-06-30", due_date = "2026-07-14",
                  delay_date = "2026-07-20", is_disputed = false,
                  items = Array.Empty<object>(), additional_charges = Array.Empty<object>(),
                  reminders = Array.Empty<object>(), documents = Array.Empty<object>(),
                  legal_basis = new { claim_type_code = "H05", contract_date = "2026-06-01", description = "Consulting agreement" }
              } }
          };
          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 matches = order.RootElement.GetProperty("claims").EnumerateArray()
              .Where(claim => claim.GetProperty("your_reference").GetString() == Environment.GetEnvironmentVariable("CLAIM_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 quickstart-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
  {
    "debtor_id": "$DEBTOR_ID",
    "additional_debtor_ids": [],
    "starting_approach": "extrajudicial",
    "creditor_obligation_fulfilled": true,
    "claims": [{
      "type": "receivable",
      "your_reference": "$CLAIM_REFERENCE",
      "document_reference": "$DOCUMENT_REFERENCE",
      "subject_matter": "Consulting services for June 2026",
      "principal_amount": {"value": "125.50", "currency": "EUR"},
      "document_date": "2026-06-30",
      "due_date": "2026-07-14",
      "delay_date": "2026-07-20",
      "is_disputed": false,
      "items": [],
      "additional_charges": [],
      "reminders": [],
      "documents": [],
      "legal_basis": {
        "claim_type_code": "H05",
        "contract_date": "2026-06-01",
        "description": "Consulting agreement"
      }
    }]
  }
  JSON
  )"
  [ "$http_status" -eq 201 ] || exit 1
  ORDER_ID="$(jq -er '.id' quickstart-response.json)" || exit 1
  CLAIM_ID="$(jq -er --arg ref "$CLAIM_REFERENCE" '[.claims[] | select(.your_reference == $ref)] | if length == 1 then .[0].id else error("expected one matching claim") end' quickstart-response.json)" || exit 1
  ```
</CodeGroup>

A draft stays fully editable until it is finalized — `PATCH /v2/orders/{id}/`
corrects any field; omitted fields and arrays stay unchanged.

## 3. Finalize exactly once

Finalize accepts an empty body or exactly `{}`; any member is
`400 validation_error` with `unknown_field`. The examples send an empty body,
so they do not need a `Content-Type` header.

<CodeGroup>
  ```python Python theme={null}
  import requests

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

  response = requests.post(
      f"{PAYWISE_API_URL}/v2/orders/{ORDER_ID}/finalize/",
      headers={
          "Authorization": f"Bearer {PAYWISE_API_KEY}",
          "Idempotency-Key": FINALIZE_COMMAND_ID,
      },
      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}/finalize/`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`,
      "Idempotency-Key": process.env.FINALIZE_COMMAND_ID,
    },
    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 QuickstartRequest {
    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") + "/finalize/"))
          .timeout(Duration.ofSeconds(30))
          .header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
          .header("Idempotency-Key", System.getenv("FINALIZE_COMMAND_ID"))
          .POST(HttpRequest.BodyPublishers.noBody()).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 QuickstartRequest
  {
      static async Task Main()
      {
          using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
          using var request = new HttpRequestMessage(HttpMethod.Post,
              $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/orders/{Environment.GetEnvironmentVariable("ORDER_ID")}/finalize/");
          request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
          request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("FINALIZE_COMMAND_ID"));
          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 --output quickstart-response.json --write-out '%{http_code}' --silent --show-error --connect-timeout 5 --max-time 30 \
    --request POST "$PAYWISE_API_URL/v2/orders/$ORDER_ID/finalize/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY" \
    --header "Idempotency-Key: $FINALIZE_COMMAND_ID"
  )"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

## 4. Accept the order in the sandbox

In production, paywise reviews and accepts a submitted order. The sandbox never
accepts one on its own, and there is no API call for it: you accept the order
in the sandbox portal. From the production portal, follow this path:

**Für Entwickler** → **Zur Sandbox** → **Mandate & Aufträge** →
**Aufträge in Prüfung** → open the order finalized in step 3 → open the
sandbox drawer (the amber **Sandbox-Tools** button at the lower right, or
`Alt+S`) → **Kontext** → **Auftrag annehmen** → **Bestätigen**

Your first **Zur Sandbox** entry activates the sandbox automatically.

The portal opens the newly created mandate. Later, the same drawer simulates
the debtor paying that mandate — *Zahlung des Schuldners an paywise*, in full
or as a partial amount — and every other counterparty step; see the
[sandbox drawer](/api-docs/case-management-api/concepts/sandbox#sandbox-drawer).

## 5. Verify acceptance and find the mandate

Acceptance freezes the order's claim set into exactly one mandate, and the
order names it: once `status` is `accepted`, the order's read-only `mandate`
field carries the mandate UUID.

The production pattern is push, not poll: subscribe a webhook to
`order.accepted` (see
[Consume Case webhooks](/api-docs/case-management-api/workflows/consume-webhooks)
for subscription creation, signature verification, and deduplication). The
event payload names both sides of the boundary: the order (`order_id`,
`order_url`) and the mandate its claims were frozen into (`mandate_id`,
`mandate_url`). Fetch `mandate_url` on your configured API host directly;
`mandate_id` equals the accepted order's `mandate` field:

```json order.accepted payload (data) theme={null}
{
  "company_id": "10000000-0000-4000-8000-000000000001",
  "order_id": "20000000-0000-4000-8000-000000000001",
  "order_url": "/v2/orders/20000000-0000-4000-8000-000000000001/",
  "claim_ids": ["30000000-0000-4000-8000-000000000001"],
  "mandate_id": "50000000-0000-4000-8000-000000000001",
  "mandate_url": "/v2/mandates/50000000-0000-4000-8000-000000000001/"
}
```

Webhook delivery is at least once. Match the stored claim UUID against
`claim_ids`, then refetch `/v2/claims/{claim_id}/`. Trust that response's
current `order_id` and nullable `mandate_id`, even when the event's `order_id`
differs from the order where you originally submitted the claim. Do not follow
an order merge chain to locate it.

In this quickstart no webhook endpoint is needed: the sandbox simulation in
step 4 accepts the order synchronously. Read the order once and take the
`mandate` UUID from the response:

<CodeGroup>
  ```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 QuickstartRequest {
    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 QuickstartRequest
  {
      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 --output quickstart-response.json --write-out '%{http_code}' --silent --show-error --connect-timeout 5 --max-time 30 \
    --request GET "$PAYWISE_API_URL/v2/orders/$ORDER_ID/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY"
  )"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

Fetch the mandate by the exact UUID in the order response's `mandate` field:

<CodeGroup>
  ```python Python theme={null}
  import requests

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

  response = requests.get(
      f"{PAYWISE_API_URL}/v2/mandates/{MANDATE_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/mandates/${process.env.MANDATE_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 QuickstartRequest {
    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/mandates/" + System.getenv("MANDATE_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 QuickstartRequest
  {
      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/mandates/{Environment.GetEnvironmentVariable("MANDATE_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 --output quickstart-response.json --write-out '%{http_code}' --silent --show-error --connect-timeout 5 --max-time 30 \
    --request GET "$PAYWISE_API_URL/v2/mandates/$MANDATE_ID/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY"
  )"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

## 6. Runnable end-to-end workflow

Install `requests`, replace the inline `PAYWISE_API_KEY` placeholder, then run
this program. The API URL is already set to the Sandbox host. Every id it
needs comes straight from a command response. Before writing, it checks HTTPS
and confirms the authenticated API reports the sandbox environment. After
finalizing the order it pauses: accept the order in the sandbox portal as in
step 4, and the program picks the acceptance up on its next poll.

```python Python workflow theme={null}
import time
import uuid
from urllib.parse import urlsplit

import requests

PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_API_KEY = "pw_sbx_your_api_key"

TIMEOUT = (5, 30)


def normalized_https_origin(url):
    parts = urlsplit(url)
    if (parts.scheme.lower() != "https" or not parts.hostname
            or parts.username is not None or parts.password is not None
            or parts.port not in (None, 443) or parts.query or parts.fragment):
        raise ValueError("PAYWISE_API_URL must be an HTTPS sandbox URL")
    return (parts.scheme.lower(), parts.hostname.lower(), parts.port or 443)


def require_status(response, expected):
    if response.status_code != expected:
        response.raise_for_status()
        raise RuntimeError(f"Expected {expected}, received {response.status_code}")


def main():
    base_url = PAYWISE_API_URL
    normalized_https_origin(base_url)
    session = requests.Session()
    session.headers.update({
        "Authorization": f"Bearer {PAYWISE_API_KEY}",
        "Accept": "application/json",
    })

    response = session.get(f"{base_url}/v2/info/", timeout=TIMEOUT)
    require_status(response, 200)
    environment = next((value for key, value in response.headers.items()
                        if key.lower() == "x-paywise-environment"), None)
    if environment != "sandbox" or response.json().get("environment") != "sandbox":
        raise RuntimeError("This quickstart requires an authenticated sandbox API")

    run_id = str(uuid.uuid4())

    debtor_payload = {
        "your_reference": f"quickstart-debtor-{run_id}",
        "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 = session.post(
        f"{base_url}/v2/debtors/",
        headers={"Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4())},
        json=debtor_payload,
        timeout=TIMEOUT,
    )
    require_status(response, 201)
    debtor_id = response.json()["id"]

    order_payload = {
        "debtor_id": debtor_id,
        "additional_debtor_ids": [],
        "starting_approach": "extrajudicial",
        "creditor_obligation_fulfilled": True,
        "claims": [{
            "type": "receivable",
            "your_reference": f"quickstart-claim-{run_id}",
            "document_reference": f"INV-{run_id}",
            "subject_matter": "Consulting services for June 2026",
            "principal_amount": {"value": "125.50", "currency": "EUR"},
            "document_date": "2026-06-30", "due_date": "2026-07-14",
            "delay_date": "2026-07-20",
            "is_disputed": False, "items": [], "additional_charges": [],
            "reminders": [], "documents": [],
            "legal_basis": {"claim_type_code": "H05", "contract_date": "2026-06-01", "description": "Consulting agreement"},
        }],
    }
    response = session.post(
        f"{base_url}/v2/orders/",
        headers={"Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4())},
        json=order_payload,
        timeout=TIMEOUT,
    )
    require_status(response, 201)
    order = response.json()
    order_id = order["id"]
    matching_claims = [
        claim for claim in order.get("claims", [])
        if claim.get("your_reference") == f"quickstart-claim-{run_id}"
    ]
    if len(matching_claims) != 1:
        raise RuntimeError("Expected exactly one claim with the submitted business reference")
    claim_id = matching_claims[0]["id"]

    response = session.post(
        f"{base_url}/v2/orders/{order_id}/finalize/",
        headers={"Idempotency-Key": str(uuid.uuid4())},
        timeout=TIMEOUT,
    )
    require_status(response, 200)

    # Sandbox only: nothing accepts a submitted order on its own, and there is
    # no API call for it. Accept the order in the sandbox portal (sandbox
    # drawer -> Kontext -> Auftrag annehmen -> Bestätigen) while this program
    # polls the order, at most 20 times, 15 seconds apart.
    print(
        f"Order {order_id} is submitted. Accept it in the sandbox portal: "
        "Mandate & Aufträge -> Aufträge in Prüfung -> open the order -> "
        "Sandbox-Tools -> Kontext -> Auftrag annehmen -> Bestätigen."
    )
    for attempt in range(20):
        if attempt:
            time.sleep(15)
        response = session.get(f"{base_url}/v2/orders/{order_id}/", timeout=TIMEOUT)
        require_status(response, 200)
        current = response.json()
        if current["status"] == "accepted":
            break
        if current["status"] != "submitted":
            raise RuntimeError(f"Expected submitted or accepted, order is {current['status']}")
    else:
        raise RuntimeError("Order was not accepted in the sandbox portal in time")
    mandate_id = current.get("mandate")
    if not mandate_id:
        raise RuntimeError("Accepted order did not return its mandate ID")

    # The same acceptance rule applies to webhook claim_ids: refetch the claim
    # and trust its current placement after review.
    response = session.get(f"{base_url}/v2/claims/{claim_id}/", timeout=TIMEOUT)
    require_status(response, 200)
    accepted_claim = response.json()
    if (accepted_claim.get("status") != "accepted" or accepted_claim.get("mandate_id") != mandate_id):
        raise RuntimeError("Claim acceptance projection mismatch")
    current_order_id = accepted_claim["order_id"]

    # Fetch the mandate and confirm it holds this run's exact claim.
    response = session.get(f"{base_url}/v2/mandates/{mandate_id}/", timeout=TIMEOUT)
    require_status(response, 200)
    mandate = response.json()
    if not any(
        claim["id"] == claim_id and claim["your_reference"] == f"quickstart-claim-{run_id}"
        for claim in mandate["claims"]
    ):
        raise RuntimeError("Mandate does not contain this run's exact claim")

    return {
        "order_id": order_id,
        "claim_id": claim_id,
        "current_order_id": current_order_id,
        "mandate_id": mandate_id,
    }


WORKFLOW_RESULT = main()
```

The accepted order names its mandate directly, so no list reconciliation is
needed: the program reads `mandate` from the accepted order, verifies that the
fetched mandate holds this run's exact claim, and never prints credentials.
Unexpected order states stop the program for inspection.

## Related reference

* [POST `/v2/debtors/`](/api-docs/case-management-api/reference/debtors/create-debtor)
* [GET `/v2/debtors/`](/api-docs/case-management-api/reference/debtors/list-debtors)
* [POST `/v2/orders/`](/api-docs/case-management-api/reference/orders/create-order)
* [PATCH `/v2/orders/{id}/`](/api-docs/case-management-api/reference/orders/update-order)
* [POST `/v2/orders/{id}/finalize/`](/api-docs/case-management-api/reference/orders/finalize-order)
* [GET `/v2/orders/{id}/`](/api-docs/case-management-api/reference/orders/get-order)
* [GET `/v2/mandates/`](/api-docs/case-management-api/reference/mandates/list-mandates)
* [GET `/v2/mandates/{id}/`](/api-docs/case-management-api/reference/mandates/get-mandate)


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