Outcome
The Partner created one draft for the exact company, finalized it only after a fresh ready/access check, and verified the exact order URL. Company, order, and claim UUIDs came directly from authoritative responses. Readiness does not block draft creation. Access and readiness are independent: access must be available for every delegated call, while readiness gates only finalization. The company UUID belongs solely inX-On-Behalf-Of-Company, never in the request body.
1. Check current access independently
Read the exact Company management URL without the delegation header.import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
response = requests.get(
f"{PAYWISE_API_URL}/partner/v2/companies/{PAYWISE_COMPANY_ID}/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/partner/v2/companies/${process.env.PAYWISE_COMPANY_ID}/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
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 DelegatedSubmissionRequest {
public static void main(String[] args) throws IOException, InterruptedException {
String url = System.getenv("PAYWISE_API_URL") + "/partner/v2/companies/" + System.getenv("PAYWISE_COMPANY_ID") + "/";
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
}
}
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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")}/partner/v2/companies/{Environment.GetEnvironmentVariable("PAYWISE_COMPANY_ID")}/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_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());
}
}
status="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
--request GET "$PAYWISE_API_URL/partner/v2/companies/$PAYWISE_COMPANY_ID/" \
--header "Authorization: Bearer $PAYWISE_PARTNER_KEY" --output company-access.json --write-out '%{http_code}')"
[ "$status" != "200" ] && exit 1
case_access == "available". Inspect readiness for the UI, but do not
use a false value to suppress draft creation.
2. Prove the sandbox before draft creation
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
response = requests.get(
f"{PAYWISE_API_URL}/partner/v2/info/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
environment = next((value for name, value in response.headers.items() if name.lower() == "x-paywise-environment"), None)
if environment != "sandbox":
raise RuntimeError("Refusing delegated write outside the sandbox")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/partner/v2/info/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const environment = response.headers.get("X-Paywise-Environment");
if (environment !== "sandbox") throw new Error("Refusing delegated write outside the sandbox");
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 DelegatedSubmissionRequest {
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") + "/partner/v2/info/"))
.timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
String environment = response.headers().firstValue("X-Paywise-Environment").orElse("");
if (!"sandbox".equals(environment)) throw new IOException("Refusing delegated write outside the sandbox");
}
}
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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")}/partner/v2/info/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_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());
if (!response.Headers.TryGetValues("X-Paywise-Environment", out var environmentValues)
|| environmentValues.Count() != 1
|| !string.Equals(environmentValues.Single(), "sandbox", StringComparison.Ordinal))
throw new HttpRequestException("Refusing delegated write outside the sandbox");
}
}
proof="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
--request GET "$PAYWISE_API_URL/partner/v2/info/" \
--header "Authorization: Bearer $PAYWISE_PARTNER_KEY" \
--dump-header - --output /dev/null --write-out '\n%{http_code}')"
status="$(printf '%s\n' "$proof" | tail -n 1)"
[ "$status" != "200" ] && exit 1
environment="$(printf '%s\n' "$proof" | awk '
{
separator = index($0, ":")
if (!separator || tolower(substr($0, 1, separator - 1)) != "x-paywise-environment") next
count++
value = substr($0, separator + 1)
sub(/\r$/, "", value)
sub(/^[ \t]*/, "", value)
sub(/[ \t]*$/, "", value)
}
END { if (count != 1) exit 1; print value }
')" || exit 1
[ "$environment" != "sandbox" ] && exit 1
:
Create or reuse the delegated debtor
Use the Case Management API with the sameX-On-Behalf-Of-Company header as the order.
The company UUID never belongs in either JSON body. Each tab parses the exact
returned debtor UUID and persists it as DEBTOR_ID.
import uuid
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
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": "Taylor", "last_name": "Debtor"},
"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_PARTNER_KEY}", "X-On-Behalf-Of-Company": PAYWISE_COMPANY_ID, "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"]))
const payload = {
your_reference: process.env.DEBTOR_REFERENCE, acting_as: "consumer",
person: { salutation: "mx", first_name: "Taylor", last_name: "Debtor" },
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_PARTNER_KEY}`, "X-On-Behalf-Of-Company": process.env.PAYWISE_COMPANY_ID, "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");
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 CreateDelegatedDebtorRequest {
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\":\"Taylor\",\"last_name\":\"Debtor\"},\"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_PARTNER_KEY"))
.header("X-On-Behalf-Of-Company", System.getenv("PAYWISE_COMPANY_ID")).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");
}
}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
class CreateDelegatedDebtorRequest {
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 = "Taylor", last_name = "Debtor" },
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_PARTNER_KEY"));
request.Headers.Add("X-On-Behalf-Of-Company", Environment.GetEnvironmentVariable("PAYWISE_COMPANY_ID"));
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();
}
}
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_PARTNER_KEY" \
--header "X-On-Behalf-Of-Company: $PAYWISE_COMPANY_ID" \
--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":"Taylor","last_name":"Debtor"},"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
Prove the sandbox before order creation
The debtor write and order write are separate commands. Re-check the authenticated environment immediately before creating the order.import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
response = requests.get(
f"{PAYWISE_API_URL}/partner/v2/info/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
environment = next((value for name, value in response.headers.items() if name.lower() == "x-paywise-environment"), None)
if environment != "sandbox":
raise RuntimeError("Refusing delegated write outside the sandbox")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/partner/v2/info/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const environment = response.headers.get("X-Paywise-Environment");
if (environment !== "sandbox") throw new Error("Refusing delegated write outside the sandbox");
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 DelegatedOrderSubmissionRequest {
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") + "/partner/v2/info/"))
.timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
String environment = response.headers().firstValue("X-Paywise-Environment").orElse("");
if (!"sandbox".equals(environment)) throw new IOException("Refusing delegated write outside the sandbox");
}
}
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedOrderSubmissionRequest {
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")}/partner/v2/info/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_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());
if (!response.Headers.TryGetValues("X-Paywise-Environment", out var environmentValues)
|| environmentValues.Count() != 1
|| !string.Equals(environmentValues.Single(), "sandbox", StringComparison.Ordinal))
throw new HttpRequestException("Refusing delegated write outside the sandbox");
}
}
proof="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
--request GET "$PAYWISE_API_URL/partner/v2/info/" \
--header "Authorization: Bearer $PAYWISE_PARTNER_KEY" \
--dump-header - --output /dev/null --write-out '\n%{http_code}')"
status="$(printf '%s\n' "$proof" | tail -n 1)"
[ "$status" != "200" ] && exit 1
environment="$(printf '%s\n' "$proof" | awk '
{
separator = index($0, ":")
if (!separator || tolower(substr($0, 1, separator - 1)) != "x-paywise-environment") next
count++
value = substr($0, separator + 1)
sub(/\r$/, "", value)
sub(/^[ \t]*/, "", value)
sub(/[ \t]*$/, "", value)
}
END { if (count != 1) exit 1; print value }
')" || exit 1
[ "$environment" != "sandbox" ] && exit 1
:
3. Create the delegated draft
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
CLAIM_REFERENCE = "quickstart-claim-80000000-0000-4000-8000-000000000001"
DEBTOR_REFERENCE = "quickstart-debtor-80000000-0000-4000-8000-000000000001"
DEBTOR_ID = debtor_id # Parsed from the immediately preceding create response.
ORDER_COMMAND_ID = "your-order-command-id"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
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": "INV-2026-0042", "subject_matter": "Consulting services for June 2026", "principal_amount": {"value": "125.50", "currency": "EUR"}, "document_date": "2026-06-30", "due_date": "2026-07-14", "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_PARTNER_KEY}", "X-On-Behalf-Of-Company": PAYWISE_COMPANY_ID, "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 = 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"]
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: "INV-2026-0042", subject_matter: "Consulting services for June 2026", principal_amount: { value: "125.50", currency: "EUR" }, document_date: "2026-06-30", due_date: "2026-07-14", 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_PARTNER_KEY}`, "X-On-Behalf-Of-Company": process.env.PAYWISE_COMPANY_ID, "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 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;
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 DelegatedSubmissionRequest {
static String jsonString(String 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 '\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 claimReference = System.getenv("CLAIM_REFERENCE");
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(claimReference) + ",\"document_reference\":\"INV-2026-0042\",\"subject_matter\":\"Consulting services for June 2026\",\"principal_amount\":{\"value\":\"125.50\",\"currency\":\"EUR\"},\"document_date\":\"2026-06-30\",\"due_date\":\"2026-07-14\",\"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_PARTNER_KEY"))
.header("X-On-Behalf-Of-Company", System.getenv("PAYWISE_COMPANY_ID")).header("Content-Type", "application/json")
.header("Idempotency-Key", System.getenv("ORDER_COMMAND_ID")).POST(HttpRequest.BodyPublishers.ofString(json)).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 201) throw new IOException(response.body());
String[] ids = responseIds(response.body(), claimReference);
String orderId = ids[0];
String claimId = ids[1];
}
}
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 DelegatedSubmissionRequest {
static async Task Main() {
var claimReference = Environment.GetEnvironmentVariable("CLAIM_REFERENCE");
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 = claimReference, document_reference = "INV-2026-0042", 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", 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 client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var request = new HttpRequestMessage(HttpMethod.Post, $"{Environment.GetEnvironmentVariable("PAYWISE_API_URL")}/v2/orders/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_KEY"));
request.Headers.Add("X-On-Behalf-Of-Company", Environment.GetEnvironmentVariable("PAYWISE_COMPANY_ID"));
request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("ORDER_COMMAND_ID"));
request.Content = JsonContent.Create(payload);
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using var response = await client.SendAsync(request, cancellation.Token);
if ((int)response.StatusCode != 201) throw new HttpRequestException(await response.Content.ReadAsStringAsync());
using var order = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var orderId = order.RootElement.GetProperty("id").GetString();
var matches = order.RootElement.GetProperty("claims").EnumerateArray()
.Where(claim => claim.GetProperty("your_reference").GetString() == claimReference)
.ToArray();
if (matches.Length != 1) throw new InvalidOperationException("Expected exactly one matching claim");
var claimId = matches[0].GetProperty("id").GetString();
}
}
order_payload="$(jq -cn --arg debtor "$DEBTOR_ID" --arg claim "$CLAIM_REFERENCE" '{debtor_id:$debtor,additional_debtor_ids:[],starting_approach:"extrajudicial",creditor_obligation_fulfilled:true,claims:[{type:"receivable",your_reference:$claim,document_reference:"INV-2026-0042",subject_matter:"Consulting services for June 2026",principal_amount:{value:"125.50",currency:"EUR"},document_date:"2026-06-30",due_date:"2026-07-14",is_disputed:false,items:[],additional_charges:[],reminders:[],documents:[],legal_basis:{claim_type_code:"H05",contract_date:"2026-06-01",description:"Consulting agreement"}}]}')"
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_PARTNER_KEY" \
--header "X-On-Behalf-Of-Company: $PAYWISE_COMPANY_ID" --header "Content-Type: application/json" \
--header "Idempotency-Key: $ORDER_COMMAND_ID" --data "$order_payload" \
--output order-created.json --write-out '%{http_code}')"
[ "$status" != "201" ] && exit 1
ORDER_ID="$(jq -er '.id' order-created.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-created.json)" || exit 1
id and claim id directly. An ambiguous create
replays the same key, body, and company header within a bounded budget.
Staged claim alternative
To build an empty delegated order in stages, send a separate logical command with a distinct stable key and the same tenant header:POST /v2/claims/ HTTP/1.1
Authorization: Bearer <partner-key>
X-On-Behalf-Of-Company: 20000000-0000-4000-8000-000000000001
Idempotency-Key: your-distinct-create-claim-key
Content-Type: application/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"}
}
201 response’s your_reference to equal the exact submitted
business reference, then persist its id. Replay only with the same key,
tenant header, and body.
4. Re-read current readiness
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
response = requests.get(
f"{PAYWISE_API_URL}/partner/v2/companies/{PAYWISE_COMPANY_ID}/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/partner/v2/companies/${process.env.PAYWISE_COMPANY_ID}/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
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 DelegatedSubmissionRequest {
public static void main(String[] args) throws IOException, InterruptedException {
String url = System.getenv("PAYWISE_API_URL") + "/partner/v2/companies/" + System.getenv("PAYWISE_COMPANY_ID") + "/";
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
}
}
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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")}/partner/v2/companies/{Environment.GetEnvironmentVariable("PAYWISE_COMPANY_ID")}/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_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());
}
}
status="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
--request GET "$PAYWISE_API_URL/partner/v2/companies/$PAYWISE_COMPANY_ID/" \
--header "Authorization: Bearer $PAYWISE_PARTNER_KEY" --output company-finalization.json --write-out '%{http_code}')"
[ "$status" != "200" ] && exit 1
5. Prove the sandbox before finalization
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
response = requests.get(
f"{PAYWISE_API_URL}/partner/v2/info/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
environment = next((value for name, value in response.headers.items() if name.lower() == "x-paywise-environment"), None)
if environment != "sandbox":
raise RuntimeError("Refusing delegated write outside the sandbox")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/partner/v2/info/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}` }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const environment = response.headers.get("X-Paywise-Environment");
if (environment !== "sandbox") throw new Error("Refusing delegated write outside the sandbox");
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 DelegatedSubmissionRequest {
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") + "/partner/v2/info/"))
.timeout(Duration.ofSeconds(30)).header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
String environment = response.headers().firstValue("X-Paywise-Environment").orElse("");
if (!"sandbox".equals(environment)) throw new IOException("Refusing delegated write outside the sandbox");
}
}
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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")}/partner/v2/info/");
request.Headers.Authorization = new("Bearer", Environment.GetEnvironmentVariable("PAYWISE_PARTNER_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());
if (!response.Headers.TryGetValues("X-Paywise-Environment", out var environmentValues)
|| environmentValues.Count() != 1
|| !string.Equals(environmentValues.Single(), "sandbox", StringComparison.Ordinal))
throw new HttpRequestException("Refusing delegated write outside the sandbox");
}
}
proof="$(curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 30 \
--request GET "$PAYWISE_API_URL/partner/v2/info/" \
--header "Authorization: Bearer $PAYWISE_PARTNER_KEY" \
--dump-header - --output /dev/null --write-out '\n%{http_code}')"
status="$(printf '%s\n' "$proof" | tail -n 1)"
[ "$status" != "200" ] && exit 1
environment="$(printf '%s\n' "$proof" | awk '
{
separator = index($0, ":")
if (!separator || tolower(substr($0, 1, separator - 1)) != "x-paywise-environment") next
count++
value = substr($0, separator + 1)
sub(/\r$/, "", value)
sub(/^[ \t]*/, "", value)
sub(/[ \t]*$/, "", value)
}
END { if (count != 1) exit 1; print value }
')" || exit 1
[ "$environment" != "sandbox" ] && exit 1
:
6. Finalize with an empty body
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
FINALIZE_COMMAND_ID = "your-finalize-command-id"
ORDER_ID = "30000000-0000-4000-8000-000000000001"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
response = requests.post(
f"{PAYWISE_API_URL}/v2/orders/{ORDER_ID}/finalize/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}", "X-On-Behalf-Of-Company": PAYWISE_COMPANY_ID, "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}")
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_PARTNER_KEY}`, "X-On-Behalf-Of-Company": process.env.PAYWISE_COMPANY_ID, "Idempotency-Key": process.env.FINALIZE_COMMAND_ID }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
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 DelegatedSubmissionRequest {
public static void main(String[] args) throws IOException, InterruptedException {
String url = System.getenv("PAYWISE_API_URL") + "/v2/orders/" + System.getenv("ORDER_ID") + "/finalize/";
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).header("X-On-Behalf-Of-Company", System.getenv("PAYWISE_COMPANY_ID"))
.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());
}
}
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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_PARTNER_KEY"));
request.Headers.Add("X-On-Behalf-Of-Company", Environment.GetEnvironmentVariable("PAYWISE_COMPANY_ID"));
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());
}
}
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_PARTNER_KEY" --header "X-On-Behalf-Of-Company: $PAYWISE_COMPANY_ID" \
--header "Idempotency-Key: $FINALIZE_COMMAND_ID" --output order-finalized.json --write-out '%{http_code}')"
[ "$status" != "200" ] && exit 1
{}; any member is
400 validation_error with unknown_field. Keep the company UUID in
X-On-Behalf-Of-Company, never in the JSON body.
7. Verify the exact delegated order URL
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
ORDER_ID = "30000000-0000-4000-8000-000000000001"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
response = requests.get(
f"{PAYWISE_API_URL}/v2/orders/{ORDER_ID}/",
headers={"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}", "X-On-Behalf-Of-Company": PAYWISE_COMPANY_ID}, timeout=(5, 30),
)
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
const response = await fetch(`${process.env.PAYWISE_API_URL.replace(/\/$/, "")}/v2/orders/${process.env.ORDER_ID}/`, {
method: "GET", headers: { Authorization: `Bearer ${process.env.PAYWISE_PARTNER_KEY}`, "X-On-Behalf-Of-Company": process.env.PAYWISE_COMPANY_ID }, signal: AbortSignal.timeout(30000),
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
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 DelegatedSubmissionRequest {
public static void main(String[] args) throws IOException, InterruptedException {
String url = System.getenv("PAYWISE_API_URL") + "/v2/orders/" + System.getenv("ORDER_ID") + "/";
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer " + System.getenv("PAYWISE_PARTNER_KEY")).header("X-On-Behalf-Of-Company", System.getenv("PAYWISE_COMPANY_ID")).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IOException(response.body());
}
}
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class DelegatedSubmissionRequest {
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_PARTNER_KEY"));
request.Headers.Add("X-On-Behalf-Of-Company", Environment.GetEnvironmentVariable("PAYWISE_COMPANY_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());
}
}
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_PARTNER_KEY" --header "X-On-Behalf-Of-Company: $PAYWISE_COMPANY_ID" \
--output order-current.json --write-out '%{http_code}')"
[ "$status" != "200" ] && exit 1
Runnable delegated submission workflow
Python workflow
import time
import uuid
import requests
PAYWISE_API_URL = "https://api-sandbox.paywise.de"
PAYWISE_PARTNER_KEY = "pw_sbx_your_partner_key"
CLAIM_REFERENCE = "quickstart-claim-80000000-0000-4000-8000-000000000001"
DEBTOR_REFERENCE = "quickstart-debtor-80000000-0000-4000-8000-000000000001"
FINALIZE_COMMAND_ID = "your-finalize-command-id"
DEBTOR_COMMAND_ID = "your-debtor-command-id"
ORDER_COMMAND_ID = "your-order-command-id"
PAYWISE_COMPANY_ID = "20000000-0000-4000-8000-000000000001"
base_url = PAYWISE_API_URL
if not base_url.startswith("https://"):
raise ValueError("PAYWISE_API_URL must be HTTPS")
company_id = str(uuid.UUID(PAYWISE_COMPANY_ID))
company_url = f"{base_url}/partner/v2/companies/{company_id}/"
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {PAYWISE_PARTNER_KEY}"})
def prove_sandbox():
response = session.get(f"{base_url}/partner/v2/info/", timeout=(5, 30))
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
environment = next((value for name, value in response.headers.items() if name.lower() == "x-paywise-environment"), None)
if environment != "sandbox":
raise RuntimeError("Refusing delegated write outside the sandbox")
def read_company():
response = session.get(company_url, timeout=(5, 30))
if response.status_code != 200:
response.raise_for_status()
raise RuntimeError(f"Expected 200, received {response.status_code}")
value = response.json()
if value.get("id") != company_id:
raise RuntimeError("Company identity mismatch")
return value
def delegated_command(path, expected_status, key, payload=None):
headers = {"X-On-Behalf-Of-Company": company_id, "Idempotency-Key": key}
if payload is not None:
headers["Content-Type"] = "application/json"
for attempt in range(3):
prove_sandbox()
try:
if payload is None:
response = session.post(f"{base_url}{path}", headers=headers, timeout=(5, 30))
else:
response = session.post(f"{base_url}{path}", headers=headers, json=payload, timeout=(5, 30))
except requests.Timeout:
if attempt == 2:
raise
time.sleep(attempt + 1)
continue
if response.status_code != expected_status:
response.raise_for_status()
raise RuntimeError(f"Expected {expected_status}, received {response.status_code}")
return response.json()
raise RuntimeError("Command retry budget exhausted")
access_state = read_company()
if access_state.get("case_access") != "available":
raise RuntimeError("Delegated Case access is unavailable")
debtor_payload = {
"your_reference": DEBTOR_REFERENCE, "acting_as": "consumer",
"person": {"salutation": "mx", "first_name": "Taylor", "last_name": "Debtor"},
"addresses": [{"street": "Example Street 12", "postal_code": "10115", "city": "Berlin", "country": "DE", "primary": True}],
}
debtor = delegated_command("/v2/debtors/", 201, DEBTOR_COMMAND_ID, debtor_payload)
debtor_id = str(uuid.UUID(debtor["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": "INV-2026-0042", "subject_matter": "Consulting services for June 2026", "principal_amount": {"value": "125.50", "currency": "EUR"}, "document_date": "2026-06-30", "due_date": "2026-07-14", "is_disputed": False, "items": [], "additional_charges": [], "reminders": [], "documents": [], "legal_basis": {"claim_type_code": "H05", "contract_date": "2026-06-01", "description": "Consulting agreement"}}],
}
created = delegated_command("/v2/orders/", 201, ORDER_COMMAND_ID, order_payload)
order_id = str(uuid.UUID(created["id"]))
claims = created.get("claims", [])
matching_claims = [claim for claim in 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"]))
final_state = read_company()
if final_state.get("case_access") != "available":
raise RuntimeError("Delegated access changed before finalization")
readiness = final_state.get("case_submission_readiness", {})
if readiness.get("ready") is not True or readiness.get("issues") != []:
WORKFLOW_RESULT = {"company_id": company_id, "order_id": order_id, "claim_id": claim_id, "status": "draft"}
else:
finalized = delegated_command(f"/v2/orders/{order_id}/finalize/", 200, FINALIZE_COMMAND_ID)
if finalized.get("id") != order_id or finalized.get("status") != "submitted":
raise RuntimeError("Finalize response identity or status mismatch")
current = session.get(f"{base_url}/v2/orders/{order_id}/", headers={"X-On-Behalf-Of-Company": company_id}, timeout=(5, 30))
if current.status_code != 200:
current.raise_for_status()
raise RuntimeError(f"Expected 200, received {current.status_code}")
current_order = current.json()
current_claims = current_order.get("claims")
matching_current_claims = [
claim for claim in current_claims or []
if claim.get("your_reference") == CLAIM_REFERENCE
]
if (
current_order.get("id") != order_id
or current_order.get("status") != "submitted"
or not isinstance(current_claims, list)
or len(matching_current_claims) != 1
or matching_current_claims[0].get("id") != claim_id
):
raise RuntimeError("Exact order or claim verification failed")
WORKFLOW_RESULT = {"company_id": company_id, "order_id": order_id, "claim_id": claim_id, "status": "submitted"}
Representative response
HTTP/1.1 200 OK
Content-Type: application/json
{
"type": "invoice",
"mandate": null,
"merged_into": null,
"confirmation_email": null,
"expires_at": null,
"rejection": null,
"id": "20000000-0000-4000-8000-000000000001",
"status": "submitted",
"your_reference": "client-claim-42",
"starting_approach": "extrajudicial",
"creditor_obligation_fulfilled": true,
"debtor": {
"metadata": [],
"events": [],
"id": "12000000-0000-4000-8000-000000000001",
"your_reference": "client-debtor-42",
"acting_as": "consumer",
"person": {
"salutation": "mx",
"first_name": "Taylor",
"last_name": "Debtor",
"birth_date": null
},
"organization": null,
"legal_form": null
},
"additional_debtors": [],
"claims": [{
"metadata": [],
"events": [],
"id": "30000000-0000-4000-8000-000000000001",
"order_id": "20000000-0000-4000-8000-000000000001",
"status": "submitted",
"mandate_id": null,
"debtor_id": "12000000-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-15",
"legal_basis": {
"claim_type_code": "H05",
"contract_date": "2026-06-01",
"description": "Consulting agreement"
},
"documents": [],
"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"
}
Failure and recovery
- Retry an ambiguous create/finalize only with the same key, body (when any), company header, and bounded attempt budget.
404can mean wrong tenant or revoked access. Refetch the exact Company and do not recreate inaccessible Partner drafts blindly.- A readiness error retains the editable draft. Remediate, re-read current readiness, and then issue the original logical finalize command.
- Any access event gates queued writes until current Company state is reconciled. Finalized cases remain available through company/staff channels.
Reconcile acceptance by claim UUID
Webhook deliveries are at least once. Onorder.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.
