> ## 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 an order

> Build and correct an invoice-order draft, finalize it safely, and recover from validation, idempotency, or withdrawal conflicts.

## Outcome

You have a validated, immutable `submitted` order and retained identifiers for
the order and its claims. You can then wait for a client request, rejection,
withdrawal, or acceptance into a mandate.

This workflow covers the standard invoice submission and uses
`ReceivableClaim` entries, each declaring the required claim
`"type": "receivable"`. Omitting the order-level `type` defaults to `invoice`. For subtype resources
and readiness rules, use [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).

## 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.
* One stable `Idempotency-Key` per logical `POST` command.

## Lifecycle context

Only `draft` orders are editable and finalizable. Finalize has no body and
makes the aggregate immutable with public state `submitted`, including during
internal review. Acceptance creates or extends a mandate and freezes the legal
debtor snapshot. A submitted order may be withdrawn only before staff review
starts.

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

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

## 1. Create an aggregate draft

<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"
  CREATE_ORDER_KEY = "your-create-order-key"
  DEBTOR_ID = debtor_id  # Parsed from the immediately preceding create response.
  DOCUMENT_REFERENCE = "INV-80000000-0000-4000-8000-000000000001"

  order_payload = {
      "debtor_id": DEBTOR_ID,
      "additional_debtor_ids": [],
      "starting_approach": "extrajudicial",
      "creditor_obligation_fulfilled": False,
      "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": CREATE_ORDER_KEY,
      },
      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 = 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 matching claim")
  claim_id = 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: false,
    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.CREATE_ORDER_KEY },
    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 orderId = order.id;
  const matchingClaims = order.claims.filter(
    (claim) => claim.your_reference === process.env.CLAIM_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 SubmitOrderRequest {
    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();
    }

    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 = "{\"debtor_id\":" + jsonString(System.getenv("DEBTOR_ID"))
          + ",\"additional_debtor_ids\":[],\"starting_approach\":\"extrajudicial\","
          + "\"creditor_obligation_fulfilled\":false,\"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("CREATE_ORDER_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[] ids = responseIds(response.body(), System.getenv("CLAIM_REFERENCE"));
      String orderId = ids[0];
      String claimId = ids[1];
    }
  }
  ```

  ```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 SubmitOrderRequest
  {
      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 = false,
              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("CREATE_ORDER_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 order = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
          var orderId = order.RootElement.GetProperty("id").GetString();
          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").GetString();
      }
  }
  ```

  ```bash cURL theme={null}
  order_payload="$(jq -n \
    --arg debtor "$DEBTOR_ID" \
    --arg claim_reference "$CLAIM_REFERENCE" \
    --arg document_reference "$DOCUMENT_REFERENCE" '
    {
      debtor_id: $debtor,
      additional_debtor_ids: [],
      starting_approach: "extrajudicial",
      creditor_obligation_fulfilled: false,
      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"
        }
      }]
    }
  ')" || exit 1
  http_status="$(curl --fail-with-body --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: $CREATE_ORDER_KEY" \
    --output order-response.json --write-out '%{http_code}' \
    --data "$order_payload")"
  [ "$http_status" -eq 201 ] || exit 1
  ORDER_ID="$(jq -er '.id' order-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 exactly one matching claim") end' order-response.json)" || exit 1
  ```
</CodeGroup>

This invoice-order payload references one existing debtor UUID, up to 10
additional debtor UUIDs, and up to 50 `ReceivableClaim` entries. Do not send
calculated total fields, interest configuration, rental-agreement or
enforceable-title fields, or `order_flow_type`.

Create or update debtor details only through the debtor resource. Order writes
contain debtor UUIDs, never debtor objects. Create new claims inline as complete
claim request objects, or attach them to a draft through the stable claim
collection shown below.

Every claim needs a default date (`delay_date`) before finalization. Send it
explicitly as above, or let the API derive it: from the claim's `reminders`
for a consumer debtor, or from `document_date` plus 32 days for a business
debtor. A consumer claim with neither `delay_date` nor reminders stays a
draft — finalization reports `claims.<id>.delay_date` as missing.

### Staged claim alternative

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.

<CodeGroup>
  ```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": "quickstart-claim-80000000-0000-4000-8000-000000000001",
      "document_reference": "INV-80000000-0000-4000-8000-000000000001",
      "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/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: "quickstart-claim-80000000-0000-4000-8000-000000000001",
    document_reference: "INV-80000000-0000-4000-8000-000000000001",
    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/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\":\"quickstart-claim-80000000-0000-4000-8000-000000000001\",\"document_reference\":\"INV-80000000-0000-4000-8000-000000000001\",\"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/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(), "quickstart-claim-80000000-0000-4000-8000-000000000001");
    }
  }
  ```

  ```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 = "quickstart-claim-80000000-0000-4000-8000-000000000001",
              document_reference = "INV-80000000-0000-4000-8000-000000000001",
              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/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":"quickstart-claim-80000000-0000-4000-8000-000000000001","document_reference":"INV-80000000-0000-4000-8000-000000000001","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"}}')"
  [ "$status" -eq 201 ] || exit 1
  CLAIM_ID="$(jq -er --arg ref "quickstart-claim-80000000-0000-4000-8000-000000000001" 'if .your_reference == $ref then .id else error("claim business reference mismatch") end' claim-created.json)" || exit 1
  ```
</CodeGroup>

## 2. Correct the draft

Patch only what changed. To change the additional debtors, send
`additional_debtor_ids` to
[PATCH `/v2/orders/{id}/`](/api-docs/case-management-api/reference/orders/update-order).
The array replaces the complete membership: include the UUID of every debtor
you want to keep. Omit the field to preserve the membership; `[]` unlinks everyone without
deleting debtor records. The same 10-debtor limit and duplicate checks apply as
at creation. This operation is available only while the order is a draft.

Existing debtor and claim data remain unchanged on unrelated patches. Patch a
draft claim by its stable top-level claim UUID; replacing a claim's complete
`additional_charges` array requires every desired replacement charge.

The order response embeds debtor summaries. Use their IDs with
[GET `/v2/debtors/{id}/`](/api-docs/case-management-api/reference/debtors/get-debtor)
for full details, and
[PATCH `/v2/debtors/{id}/`](/api-docs/case-management-api/reference/debtors/update-debtor)
to correct debtor information. When updating membership based on a previous
order read, send its `ETag` in `If-Match` to avoid overwriting a concurrent edit.

For a claim correction, patch the stable top-level claim resource directly:

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

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

  response = requests.patch(
      f"{PAYWISE_API_URL}/v2/claims/{CLAIM_ID}/",
      headers={"Authorization": f"Bearer {PAYWISE_API_KEY}", "Content-Type": "application/json"},
      json={"subject_matter": "Consulting services for June 2026"},
      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/claims/${process.env.CLAIM_ID}/`, {
    method: "PATCH",
    headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ subject_matter: "Consulting services for June 2026" }),
    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 CorrectClaimRequest {
    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/claims/" + System.getenv("CLAIM_ID") + "/"))
          .timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
          .header("Content-Type", "application/json")
          .method("PATCH", HttpRequest.BodyPublishers.ofString("{\"subject_matter\":\"Consulting services for June 2026\"}"))
          .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.Net.Http.Json;
  using System.Threading;
  using System.Threading.Tasks;

  class CorrectClaimRequest
  {
      static async Task Main()
      {
          using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
          using var request = new HttpRequestMessage(HttpMethod.Patch,
              $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/claims/{Environment.GetEnvironmentVariable("CLAIM_ID")}/");
          request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
          request.Content = JsonContent.Create(new { subject_matter = "Consulting services for June 2026" });
          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 PATCH "$PAYWISE_API_URL/v2/claims/$CLAIM_ID/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY" \
    --header "Content-Type: application/json" \
    --output claim-response.json --write-out '%{http_code}' \
    --data '{"subject_matter":"Consulting services for June 2026"}')"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

## 3. Finalize without a request body

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

  PAYWISE_API_URL = "https://api-sandbox.paywise.de"
  PAYWISE_API_KEY = "pw_sbx_your_api_key"
  FINALIZE_KEY = "your-finalize-key"
  ORDER_ID = "30000000-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_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}/finalize/`, {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`, "Idempotency-Key": process.env.FINALIZE_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 FinalizeOrderRequest {
    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_KEY"))
          .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 FinalizeOrderRequest
  {
      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_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 POST "$PAYWISE_API_URL/v2/orders/$ORDER_ID/finalize/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY" \
    --header "Idempotency-Key: $FINALIZE_KEY" \
    --output finalized-order.json --write-out '%{http_code}')"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

Finalize accepts an empty body or exactly `{}`; the cURL example uses the empty
form, so it sends neither `--data` nor `Content-Type`. After a timeout, fetch
the order by ID first. Record any observed `submitted`, `awaiting_client_response`,
`accepted`, `rejected`, `withdrawn`, or `expired` outcome. If it is still
`draft`, make an explicit retry decision with the same `FINALIZE_KEY`; never
issue a second uncontrolled finalize write.

## Representative response

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "confirmation_email": null,
  "expires_at": null,
  "rejection": null,
  "id": "20000000-0000-4000-8000-000000000001",
  "type": "invoice",
  "status": "submitted",
  "mandate": null,
  "merged_into": null,
  "your_reference": "client-claim-42",
  "starting_approach": "extrajudicial",
  "creditor_obligation_fulfilled": true,
  "debtor": {
    "id": "10000000-0000-4000-8000-000000000001",
    "your_reference": "quickstart-customer-1001",
    "acting_as": "consumer",
    "person": {
      "salutation": "mx",
      "first_name": "Alex",
      "last_name": "Example",
      "birth_date": null
    },
    "organization": null,
    "legal_form": null,
    "metadata": [],
    "events": []
  },
  "additional_debtors": [],
  "claims": [
    {
      "id": "30000000-0000-4000-8000-000000000001",
      "order_id": "20000000-0000-4000-8000-000000000001",
      "status": "submitted",
      "mandate_id": null,
      "debtor_id": "10000000-0000-4000-8000-000000000001",
      "additional_debtor_ids": [],
      "payments": [],
      "type": "receivable",
      "your_reference": "client-claim-42",
      "document_reference": "INV-2026-0042",
      "subject_matter": "Consulting services for June 2026",
      "is_disputed": false,
      "dispute_reason": null,
      "principal_amount": {"value": "125.50", "currency": "EUR"},
      "items": [],
      "additional_charges": [],
      "additional_charges_amount": {"value": "0.00", "currency": "EUR"},
      "total_amount": {"value": "125.50", "currency": "EUR"},
      "document_date": "2026-06-30",
      "due_date": "2026-07-14",
      "reminders": [],
      "delay_date": "2026-07-20",
      "legal_basis": {
        "claim_type_code": "H05",
        "contract_date": "2026-06-01",
        "description": "Consulting agreement"
      },
      "documents": [],
      "metadata": [],
      "events": [],
      "created_at": "2026-08-27T10:00:00Z",
      "updated_at": "2026-08-27T10:04:00Z"
    }
  ],
  "totals": {
    "order_value": {"value": "125.50", "currency": "EUR"},
    "main_claims": {"value": "125.50", "currency": "EUR"},
    "charges": {"value": "0.00", "currency": "EUR"},
    "payments": {"value": "0.00", "currency": "EUR"}
  },
  "created_at": "2026-08-27T10:00:00Z",
  "updated_at": "2026-08-27T10:05:00Z"
}
```

This is a complete response matching the `Order` response schema.

## Runnable Python workflow

This orchestration proves the sandbox before any write, 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"
CLAIM_REFERENCE = "quickstart-claim-80000000-0000-4000-8000-000000000001"
DEBTOR_ID = "10000000-0000-4000-8000-000000000001"
DOCUMENT_REFERENCE = "INV-80000000-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")

order_payload = {
    "debtor_id": DEBTOR_ID,
    "additional_debtor_ids": [],
    "starting_approach": "extrajudicial",
    "creditor_obligation_fulfilled": False,
    "claims": [{
        "type": "receivable",
        "your_reference": CLAIM_REFERENCE,
        "document_reference": DOCUMENT_REFERENCE,
        "subject_matter": "Consulting services for May 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,
    }],
}
create_key = str(uuid.uuid4())
created = requests.post(
    f"{base_url}/v2/orders/",
    headers={**headers, "Idempotency-Key": create_key},
    json=order_payload,
    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("status") != "draft":
    raise RuntimeError("Created order is not editable")
order_id = 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 = matching_claims[0]["id"]

corrected = requests.patch(
    f"{base_url}/v2/claims/{claim_id}/",
    headers=headers,
    json={"subject_matter": "Consulting services for June 2026"},
    timeout=(5, 30),
)
if corrected.status_code != 200:
    corrected.raise_for_status()
    raise RuntimeError(f"Expected 200, received {corrected.status_code}")

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`: correct the reported field paths on the draft, then
  finalize again with a new logical-command key. The failed validation did not
  finalize the order. Common causes on create and `PATCH`: a missing
  `debtor_id` (`debtor_id` / `required`), a non-string, malformed, or empty
  UUID (`debtor_id` / `invalid`, or `additional_debtor_ids[i]` / `invalid`),
  a duplicate party (`additional_debtor_ids` / `duplicate`),
  `creditor_obligation_fulfilled` or `is_disputed` sent
  as a string or number instead of a JSON boolean (`invalid`), a date with
  non-ASCII digits (`invalid`), or a control character in a text field
  (`control_characters`). An unknown or other-company debtor UUID is a neutral
  `404`, not a `400` field error.
* `403 terms_acceptance_required` on finalize: the credential's current API
  terms have not been accepted. Accept them in the developer portal; the
  draft stays editable and finalize again with a fresh key.
* Finalize timeout, transient `500`, or `503`: fetch the exact order by ID
  before making any retry decision. Record every valid non-draft lifecycle
  outcome; only an observed `draft` can be considered for an explicit retry
  with the same key. Do not immediately repeat finalize.
* For other ambiguous idempotent POST outcomes, reconcile the resource first;
  if a retry is still required, repeat the same request with the same key.
* `409 idempotency_request_in_progress`: wait for `Retry-After`, then retry the
  same request and key. `idempotency_key_expired` is terminal; reconcile by
  fetching the order. Reusing a key for a different operation or body also
  returns `409` and requires a new logical command.
* Lifecycle `409`: refetch the order. It may already be finalized
  (`Order is already finalized.`), withdrawn or decided
  (`Order is already finalized or withdrawn.` — also when finalize raced a
  withdrawal), or merged into another order
  (`Order was merged into another order.`, on every write; follow
  `merged_into`). Another actor may have changed it.
* To withdraw an eligible submitted order, use a fresh command key:

<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 = "30000000-0000-4000-8000-000000000001"
  WITHDRAW_KEY = "your-withdraw-key"

  response = requests.post(
      f"{PAYWISE_API_URL}/v2/orders/{ORDER_ID}/withdraw/",
      headers={"Authorization": f"Bearer {PAYWISE_API_KEY}", "Content-Type": "application/json", "Idempotency-Key": WITHDRAW_KEY},
      json={"reason": "Submitted duplicate invoice in error"},
      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}/withdraw/`, {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.PAYWISE_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": process.env.WITHDRAW_KEY },
    body: JSON.stringify({ reason: "Submitted duplicate invoice in error" }),
    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 WithdrawOrderRequest {
    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") + "/withdraw/"))
          .timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_API_KEY"))
          .header("Content-Type", "application/json").header("Idempotency-Key", System.getenv("WITHDRAW_KEY"))
          .POST(HttpRequest.BodyPublishers.ofString("{\"reason\":\"Submitted duplicate invoice in error\"}"))
          .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.Net.Http.Json;
  using System.Threading;
  using System.Threading.Tasks;

  class WithdrawOrderRequest
  {
      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")}/withdraw/");
          request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_API_KEY"));
          request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("WITHDRAW_KEY"));
          request.Content = JsonContent.Create(new { reason = "Submitted duplicate invoice in error" });
          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 POST "$PAYWISE_API_URL/v2/orders/$ORDER_ID/withdraw/" \
    --header "Authorization: Bearer $PAYWISE_API_KEY" \
    --header "Content-Type: application/json" \
    --header "Idempotency-Key: $WITHDRAW_KEY" \
    --output withdrawn-order.json --write-out '%{http_code}' \
    --data '{"reason":"Submitted duplicate invoice in error"}')"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

Withdrawal returns `409` after staff review starts and for draft, accepted,
rejected, expired, or already withdrawn orders. Refetch and stop; do not loop.

## Verify

<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 = "30000000-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 ReadOrderRequest {
    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 ReadOrderRequest
  {
      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 current-order.json --write-out '%{http_code}')"
  [ "$http_status" -eq 200 ] || exit 1
  ```
</CodeGroup>

Then require `.status` to be `submitted` or `accepted` before recording the
workflow as successful.

Also retain `X-Paywise-Request-Id` for support and the finalization key until
the outcome is durably recorded.

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

* [POST `/v2/orders/`](/api-docs/case-management-api/reference/orders/create-order)
* [PATCH `/v2/claims/{id}/`](/api-docs/case-management-api/reference/claims/update-claim)
* [POST `/v2/orders/{id}/finalize/`](/api-docs/case-management-api/reference/orders/finalize-order)
* [POST `/v2/orders/{id}/withdraw/`](/api-docs/case-management-api/reference/orders/withdraw-order)
* [GET `/v2/orders/{id}/`](/api-docs/case-management-api/reference/orders/get-order)


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