Financing & Solvency
When an end customer wants to finance a quote rather than pay outright, the quote enters a
financing & solvency review. This guide walks through the flow an integration drives over
the API: request financing for a quote, answer the lender's information and document requests,
and receive the solvency decision. Every request is authenticated with your
X-Api-Key header.
The financing lifecycle
A financing case moves through the states below — a filtered view of the quote state
machine, showing only its finance_* stages. The diagram is
generated directly from the code, so it always matches the API. Most transitions are driven
internally by the lender's review (via Kairos and the Lynck solvency check); the calls in
this guide are the ones you make. The full state machine is also on the
Lifecycle page.
Financing Flow
The financing states of a quote (a filtered view of the quote lifecycle), from finance review to acceptance.
graph TB
place0(["accepted_by_customer"])
place1(("finance_review"))
place2(("in_progress"))
place3(("finance_accepted"))
place4(("manual_finance_review"))
place5(("finance_questions"))
place6(("finance_rejected"))
place7(("completed"))
place0-->|"request_finance_review"|place1
place2-->|"request_finance_review_direct"|place1
place1-->|"finance_accept"|place3
place4-->|"finance_accept"|place3
place5-->|"finance_accept"|place3
place6-->|"finance_accept"|place3
place3-->|"complete"|place7
place1-->|"finance_needs_help"|place5
place4-->|"finance_needs_help"|place5
place0-->|"start_manual_finance_review"|place4
place2-->|"start_manual_finance_review"|place4
place1-->|"finance_reject"|place6
place4-->|"finance_reject"|place6
place5-->|"finance_reject"|place6
place6-->|"finance_resume"|place3
Transitions (text alternative)
| Transition | From | To |
|---|---|---|
| request_finance_review | accepted_by_customer | finance_review |
| request_finance_review_direct | in_progress | finance_review |
| finance_accept | finance_review | finance_accepted |
| finance_accept | manual_finance_review | finance_accepted |
| finance_accept | finance_questions | finance_accepted |
| finance_accept | finance_rejected | finance_accepted |
| complete | finance_accepted | completed |
| finance_needs_help | finance_review | finance_questions |
| finance_needs_help | manual_finance_review | finance_questions |
| start_manual_finance_review | accepted_by_customer | manual_finance_review |
| start_manual_finance_review | in_progress | manual_finance_review |
| finance_reject | finance_review | finance_rejected |
| finance_reject | manual_finance_review | finance_rejected |
| finance_reject | finance_questions | finance_rejected |
| finance_resume | finance_rejected | finance_accepted |
Build it step by step
POST
/api/carts/{id}/checkout
Financing starts by checking out a cart with a financingType
(leasing, rent_to_own,
rent or a Print-Plan variant). Print-Plan variants require every
cart item to reference a Print-Plan-capable category (hasPrintPlanCapability
in GET /api/product-categories); otherwise the checkout is rejected
with 422 and nothing is created. Checkout creates the quote and
its financing case and submits it for finance review. The response returns the
financeCaseId — keep it; every step below addresses this case.
Build the cart first with POST /api/carts (see the
Cart reference);
for the non-financing path see the
Create a Quote guide.
curl -X POST 'https://sandbox.partner.miete24.com/api/carts/42/checkout' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"duration": 36,
"financingType": "leasing",
"paymentInterval": "monthly",
"customer": {
"firstName": "Anna",
"lastName": "Schmidt",
"email": "anna.schmidt@example.com",
"street": "Hauptstrasse",
"houseNumber": "12a",
"zipCode": "10115",
"city": "Berlin",
"country": "Deutschland",
"company": "Schmidt GmbH",
"vatId": "DE123456789"
},
"deliveryAddress": {
"recipientName": "David Bühler",
"street": "Weingartenstraße",
"houseNumber": "5",
"zipCode": "79211",
"city": "Denzlingen",
"country": "Deutschland"
}
}'
const response = await fetch('https://sandbox.partner.miete24.com/api/carts/42/checkout', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
duration: 36,
financingType: 'leasing',
paymentInterval: 'monthly',
customer: {
firstName: 'Anna',
lastName: 'Schmidt',
email: 'anna.schmidt@example.com',
street: 'Hauptstrasse',
houseNumber: '12a',
zipCode: '10115',
city: 'Berlin',
country: 'Deutschland',
company: 'Schmidt GmbH',
vatId: 'DE123456789',
},
deliveryAddress: {
recipientName: 'David Bühler',
street: 'Weingartenstraße',
houseNumber: '5',
zipCode: '79211',
city: 'Denzlingen',
country: 'Deutschland',
},
}),
});
const checkout = await response.json();
{
"financeCaseId": "019e964c-fb93-73a6-a672-254c170c72d9",
"financeCaseLink": "https://app.example.com/partner/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9",
"message": "Quote successfully created"
}
Delivery address:
the customer block is the lessee — its company is mandatory and
its address is the contact and billing address. To ship somewhere else, add the optional
deliveryAddress block with its own
recipientName (mandatory) and, only for a business
delivery target, a company. The quote, the contract and the
offer PDF then show exactly that recipient; the lessee company is not prepended, and the
customer's master data is never changed. Without the block the customer address is the
delivery address, headed by the customer's company and contact person. In API responses
the resolved delivery address appears as the quote's shippingAddress
— the two terms name the same thing.
Quote only, without starting financing:
send "submitFinancing": false in the same checkout body
(optional boolean, default true). The checkout then only
creates the quote, like create quote in the PartnerHub: the financing case stays
in status in_progress with financingRequest: false,
nothing is submitted to the lender and no e-mail goes to the customer. The partner completes
the quote, sends it or starts the financing request in the PartnerHub; follow the status via
GET /api/financing_cases/{id} or the
financing_case.status_changed webhook. This works for every
financing type, including Print-Plan. A value other than true or
false is rejected with 400.
{
"duration": 36,
"financingType": "leasing",
"paymentInterval": "monthly",
"submitFinancing": false,
"customer": { "...": "as above" }
}
{
"financeCaseId": "019e964c-fb93-73a6-a672-254c170c72d9",
"financeCaseLink": "https://app.example.com/partner/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9",
"message": "Quote created without financing request"
}
Errors: the checkout rejects a request before anything is created when it can tell up front that the order cannot go ahead:
| Status | When |
|---|---|
| 400 | The product sector is not enabled for your account, a category id or slug is unknown or belongs to another sector, the financing type is not offered for this duration, the duration is outside the depreciation (AfA) range of the products, or a rent quote has no valid custom SLA. |
| 401 | The API key is missing or invalid. |
| 403 | The cart belongs to another partner. |
| 404 | The cart does not exist. |
| 409 | The cart has already been checked out, a checkout for it is still running, or the quoteNumber is already used for one of your quotes. |
| 422 | A field is missing, invalid or unknown, the payment interval or commission tier is not available to you, a Print-Plan rule is not met, or an Idempotency-Key is reused with a different body. It is also returned when the cart items have different article types: the response then holds one violation per item with the code MIXED_ARTICLE_TYPES (this also applies to carts created before the rule); create one cart per article type. A detail starting with partner_financing_not_ready: means your partner account is still being set up by Miete24 (no financing partner assigned yet) — the cart stays active and the same request succeeds once the setup is complete. |
PartnerHub does not check product availability at checkout: products are created from the item data you send. Whether the order goes ahead is decided in the review after the checkout. To learn right away when a case is canceled or rejected, subscribe to webhooks.
Full parameters, all operations & code samples → Cart reference
GET
/api/financing-cases/{financingCaseId}/document-requests
During the review the lender may ask for additional information or documents. Poll this
endpoint to see everything that is outstanding for the case: each item has a
type (information,
document or fill_and_return), a
category, a status, and when present
a callbackRequestId linking to the partner callback inbox. You then
answer each one with the matching call below.
curl -X GET 'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests' \
-H 'X-Api-Key: YOUR_API_KEY'
const response = await fetch(
'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests',
{ method: 'GET', headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
);
const requests = await response.json();
[
{
"id": 12,
"type": "information",
"label": "Shareholder structure",
"status": "pending",
"category": "shareholder_information",
"templateDownloadUrl": null,
"callbackRequestId": 87,
"callbackStatus": "new",
"requestedAt": "2025-11-07T12:00:00+00:00"
},
{
"id": 34,
"type": "document",
"label": "Business registration",
"status": "pending",
"category": "business_registration",
"templateDownloadUrl": null,
"callbackRequestId": 87,
"callbackStatus": "new",
"requestedAt": "2025-11-07T12:00:00+00:00"
}
]
POST
/api/financing-cases/{financingCaseId}/document-requests/information/{informationId}/respond
Answer an information request by posting the text response,
using the request's id from the list above as
informationId.
curl -X POST 'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests/information/12/respond' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"response": "Managing director Anna Schmidt holds 100% of the shares."
}'
const response = await fetch(
'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests/information/12/respond',
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
response: 'Managing director Anna Schmidt holds 100% of the shares.',
}),
},
);
const item = await response.json();
POST
/api/financing-cases/{financingCaseId}/document-requests/{type}/{itemId}/not-available
Mark an optional request as not available (liegt mir nicht vor). Use the item
type and id from the list. Only
optional, pending, partner-visible items are accepted; wrong partner scope returns
403.
curl -X POST 'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests/document/34/not-available' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json'
const response = await fetch(
'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/document-requests/document/34/not-available',
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
},
);
POST
/api/financing-cases/{financingCaseId}/documents
Answer a document request by uploading the file as
multipart/form-data. file (PDF,
PNG or JPG, max 10 MB) and category are required; pass the
matching request's id as requestedDocumentId to fulfil a
specific request.
curl -X POST 'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/documents' \
-H 'X-Api-Key: YOUR_API_KEY' \
-F 'file=@business_registration.pdf' \
-F 'category=business_registration' \
-F 'description=Handelsregisterauszug 2024' \
-F 'requestedDocumentId=34'
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('category', 'business_registration');
form.append('description', 'Handelsregisterauszug 2024');
form.append('requestedDocumentId', '34');
const response = await fetch(
'https://sandbox.partner.miete24.com/api/financing-cases/019e964c-fb93-73a6-a672-254c170c72d9/documents',
{
method: 'POST',
headers: { 'X-Api-Key': 'YOUR_API_KEY' },
body: form,
},
);
const document = await response.json();
{
"id": 42,
"financingCaseId": "019e964c-fb93-73a6-a672-254c170c72d9",
"originalName": "business_registration.pdf",
"mimeType": "application/pdf",
"fileSize": 1048576,
"category": "business_registration",
"description": "Handelsregisterauszug 2024",
"uploadedAt": "2025-11-07T12:30:00+00:00",
"downloadUrl": "/api/documents/42/download"
}
Errors:
422 when the file is missing, too large, not a PDF/PNG/JPG,
or the category is not a document category;
404 for an unknown financing case or request id;
401 for a missing or invalid API key.
All request operations → Document Request reference · Upload & document metadata → Financing Document reference
WEBHOOK
finance status update → your endpoint
When the solvency review concludes, the case moves to
finance_accepted or finance_rejected
(or to finance_questions / manual_finance_review
if the lender needs more). You do not call an endpoint for the decision —
instead, PartnerHub POSTs a financing_case.status_changed
event to a webhook endpoint you register, with the new status in
data.toStatus. The same event tells you when a case is canceled.
Registration, signature check and retries are explained in the
Webhooks
guide. Every event has this shape:
{
"id": "01K70Q8V6M3J5Z2R9T4X7B1C8D",
"event": "financing_case.status_changed",
"occurredAt": "2026-10-07T09:15:00+02:00",
"version": 1,
"data": {
"financingCaseId": "019e964c-fb93-73a6-a672-254c170c72d9",
"fromStatus": "finance_review",
"toStatus": "canceled",
"status": "canceled",
"transition": "cancel",
"quoteId": "019e964c-fb94-7c1e-9f0a-3b5d8e2a6c41"
}
}
If you prefer to pull instead of receiving webhooks, read the case at any time and inspect
its status:
curl -X GET 'https://sandbox.partner.miete24.com/api/financing_cases/019e964c-fb93-73a6-a672-254c170c72d9' \
-H 'X-Api-Key: YOUR_API_KEY'
const response = await fetch(
'https://sandbox.partner.miete24.com/api/financing_cases/019e964c-fb93-73a6-a672-254c170c72d9',
{ method: 'GET', headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
);
const financingCase = await response.json();
{
"@id": "/api/financing_cases/019e964c-fb93-73a6-a672-254c170c72d9",
"id": "019e964c-fb93-73a6-a672-254c170c72d9",
"caseNumber": "FC-2025-000123",
"type": "quote",
"status": "finance_accepted"
}
Errors:
404 for an unknown financing case id;
401 for a missing or invalid API key.
Full fields, all operations & code samples → Financing Case reference
Sandbox: simulate a solvency decision
In the sandbox there is no real lender, so the case never leaves
finance_review on its own. To test both branches of your
integration — an approved and a declined solvency check — drive the decision yourself with
the sandbox-only endpoint below. It moves the case to
finance_accepted or finance_rejected
end-to-end, firing the same downstream subscribers (partner mails, webhooks) as a real Kairos
decision — without an HMAC signature, response hash or Kairos/Lynck call. It requires a
sandbox API key and only operates on your own financing cases.
POST
/api/sandbox/financing_cases/{id}/finance-decision
curl -X POST 'https://sandbox.partner.miete24.com/api/sandbox/financing_cases/019e964c-fb93-73a6-a672-254c170c72d9/finance-decision' \
-H 'X-Api-Key: YOUR_SANDBOX_API_KEY' \
-H 'Content-Type: application/json' \
-d '{ "decision": "accepted" }'
const response = await fetch(
'https://sandbox.partner.miete24.com/api/sandbox/financing_cases/019e964c-fb93-73a6-a672-254c170c72d9/finance-decision',
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_SANDBOX_API_KEY',
'Content-Type': 'application/json',
},
// Use "rejected" to test the declined-solvency branch.
body: JSON.stringify({ decision: 'accepted' }),
},
);
// 204 No Content on success.
Responses:
204 on success;
422 when the case is in a non-decidable state;
404 outside the sandbox environment, where the endpoint is
inactive;
401 for a missing or invalid API key.
For every simulate scenario (document requests, contract places, e-signature states, reset), see the Sandbox Simulate guide. · All sandbox endpoints → Sandbox reference
What's next
- Browse every endpoint and schema in the API Reference.
- See the complete state machine on the Lifecycle page.
- Get notified of every status change instead of polling — see the Webhooks guide.
- New to the API? Start with the Create a Quote guide.