Skip to main content

Outcome

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

Prerequisites

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

Lifecycle context

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

0. Create or reuse the debtor

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

1. Create inline

Use one POST /v2/orders/ when the agreement, claim, and files are ready together. The nested operation is atomic.
contract_date is the date the rental contract was concluded. It is not the move-in date or the beginning of the tenancy. monthly_rent is the recurring Warmmiete including ancillary-cost prepayments; it provides agreement context and does not cap or determine an individual claim amount.

2. Let dates derive—or override them deliberately

When rental_agreement.due_rule is omitted or null, both H17 and H19 claims default to the third German business day of the rental month. German business days are Monday through Friday excluding German federal holidays. For each H17/H19 claim:
  • document_date must be the first day of the claimed month.
  • Omitted legal_basis.contract_date is copied from the agreement. A different supplied value returns 400.
  • Omitted due_date is derived from the agreement rule.
  • Omitted delay_date becomes the day after the resolved due date.
  • An explicit delay_date must be later than the resolved due_date.
  • Explicit due and delay dates remain claim-level overrides. Patching either field to null restores server derivation.
  • A later agreement due-rule change recomputes derived dates and preserves explicit overrides — with one exception: an explicit delay_date that falls on or before the recomputed due_date is reset to due_date + 1 and stops being an override. Reminders are informational and do not change this rule.
  • A rental-agreement contract_date (set inline or through a later agreement PATCH) is written to every rental claim’s legal_basis.contract_date, so an order whose contract date arrives through the agreement satisfies readiness.
  • Every date is YYYY-MM-DD in ASCII digits; 9999-12-30 is the latest accepted due_date (delay_date is its successor) and the latest accepted reminder date/due_date; a later value is 400 on that field with code invalid.
One order cannot mix H17 and H19 or contain two rental claims for the same month. A mixed family returns 400; a duplicate month returns 409, so patch the existing claim instead. Other ReceivableClaim entries may coexist with the rental claims.

3. Build the draft in stages

Create the empty draft first, then use a distinct stable idempotency key for this logical claim command. Preserve the exact key and body for replay, and store the returned claim UUID after verifying its exact business reference.
For a commercial tenancy, create the same empty rental draft, update its agreement as needed, and send a complete H17 claim to the same collection. For example, the claim body still includes the draft UUID:
Keep one primary rental_agreement document on the agreement and one relevant claim_statement on each rental claim. A second primary of the same type conflicts with 409; delete the old draft document before replacing it. Agreement documents are optional at finalization, but every claim still needs a non-empty subject_matter or a supporting document.

4. Finalize or remove the draft

Finalize with an empty-body POST /v2/orders/{id}/finalize/ and a fresh Idempotency-Key. A rental draft is incomplete until it has a complete property address, positive monthly rent, contract date, and at least one H17/H19 claim. Missing readiness data returns 422 with public paths such as rental_agreement.contract_date; invalid commands return 400, and conflicts return 409. Read the order after any ambiguous result. Once submitted, it may be withdrawn while still eligible. If the draft should never be submitted, use DELETE /v2/orders/{id}/ instead; draft deletion also removes its rental agreement.

Runnable Python workflow

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

Failure and recovery

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

Verify

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

Reconcile acceptance by claim UUID

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