Skip to main content
Errors are English-only and machine-readable on all three APIs. Branch on code and nested error codes; reserve detail and message for logs and developer-facing output. Nested field values are JSON paths into the request body (claims[0].principal_amount, debtor_id, additional_debtor_ids[0]) or, for query strings, the parameter name.

Retry writes safely

Every effectful POST requires Idempotency-Key. Generate one opaque key per logical command and retain it until the outcome is known.
The finalize operation accepts an empty body or exactly {}. A successful call returns the complete Order representation defined by the operation reference; the fully valid error above shows a lifecycle conflict that must be resolved rather than blindly retried. The key is scoped by tenant, method, operation, and normalized path. For Case Management commands the tenant is the selected company; for Partner management commands it is the Partner owner. Direct and Partner credentials authorized for the same Case company share its retry namespace. The path is compared case-insensitively, so an upper-case resource id names the same command as its lower-case form. Equivalent JSON object-key order and multipart part order replay; array order remains significant. Rotating an API secret or replacing a token preserves retry protection. Retry with the same Idempotency-Key and request after changing credentials; the replacement credential must still have the operation’s required scopes and current access to the tenant. A changed request returns 409 idempotency_key_conflict. Use different keys for different logical commands, including when several integrations act for the same company.
  • An exact completed retry replays the original status, body, and application headers and adds Idempotency-Replayed: true.
  • The same key with a different request returns typed 409.
  • A retry while the original call is running waits up to 2 seconds for it. If the original finishes in that time, the retry receives its response with Idempotency-Replayed: true; otherwise it returns 409 idempotency_request_in_progress with Retry-After: 2.
  • A completed response replays for 24 hours. After that the key answers 409 idempotency_key_expired for another 24 hours and is then forgotten (48 hours in total): a request with it executes as a new command. When a Partner’s access to a company ends, its stored responses for that company expire at once: they answer 409 idempotency_key_expired for 24 hours and are then forgotten.
  • A 5xx outcome is never stored: a failure raised inside the command — including 503 service_unavailable from a transient database or storage fault — releases the key, so retrying the same key re-executes the command instead of replaying the failure. Only an error status the command itself returns as its result claims the key. The same release applies to a raised 4xx: a corrected retry under the same key executes normally.
  • The header holds exactly one key; a value containing , (typically two Idempotency-Key headers folded together) is 400 validation_error.
Domain writes, webhook deliveries, and the stored response commit together; paywise starts its internal follow-up work after that commit, and a replayed request never repeats an effect. After a server-side failure the same key can be retried at once. After a network timeout, retry with the same key—never generate another key for the same logical command.

Recover by status

Common error codes

The top-level code names the failure class; nested errors[].code names the rule a field broke. Codes you should branch on beyond required, invalid, blank, max_length, invalid_choice, and unknown_field: Record the fresh X-Paywise-Request-Id when contacting support. Caller-supplied request IDs are ignored. Validation failures and idempotent replays consume rate-limit capacity; authentication, tenant-context, and access failures do not.

Inspect rate-limit headroom

GET /v2/usage/ and GET /partner/v2/usage/ return the live buckets used for enforcement. Usage and info are ordinary reads, consume principal capacity, and can return 429; usage includes its own request in the used count. Direct Case and Mahnservice requests charge credential and company buckets; Partner management charges credential and Partner; Partner-on-behalf Case requests charge credential, Partner, and selected company. Each allows 600 requests per minute. Reads have no per-operation bucket; every write also charges endpoint:<basename>.<action> at 120 per minute. Webhook test delivery and manual redelivery share a company-scoped 20-per-hour expensive-operation bucket. Expensive buckets are checked before the operation and charged only after a qualifying success (200 download, 202 accepted dispatch); other outcomes do not charge. They do not reserve capacity for in-flight work, so concurrent successes can all pass the pre-check. Once the stored bucket is full, later requests answer 429 up front. There are no X-RateLimit-* headers. If the shared counter store is temporarily unavailable, normal enforcement fails open and emits an operational signal; the usage operation fails closed with typed 503 instead of returning fabricated counters. See finalize an order, Case usage, and Partner usage for the current operation contracts.