Source: https://docs.debitura.com/api/referral-partner-api/cases

# Cases — Referral Partner API

Base URL: `https://referral-api.debitura.com`

Authentication: `XApiKey` API key; see [authentication](https://docs.debitura.com/developer-concepts/authentication.md).

Generated from [OpenAPI](https://docs.debitura.com/openapi/referral-partner-api.json). Examples are schema illustrations, not observed responses. Complete schemas, including recursive types, are in OpenAPI.

<a id="list-cases-for-referral-partner-clients"></a>

## List cases for referral partner clients

`GET /cases`

Retrieves a paginated list of debt collection cases for all clients linked to the authenticated referral partner.

Case Lifecycle Stages
Cases progress through a defined lifecycle:
• **Pending contract signing** - Client hasn't signed debt collection agreement yet
• **Pending Verification Internal** - Case awaiting internal Debitura review
• **Pending Verification** - Case awaiting partner verification
• **More Info Required** - More information required from creditor
• **Collecting Quotes** - Awaiting quote from collection partner
• **Pending Quote Selection** - Quote provided by partner
• **Active** - Case is being actively collected
• **Paused** - Collection temporarily paused
• **Closed** - Case is closed (check CloseCode for reason)

Filtering Options
• **FromDate/ToDate** - Filter by case creation date range (ISO 8601)
• **ExternalTenantId** - Filter to specific client using your identifier
• **Statuses** - Filter by lifecycle status(es)
• **Query** - Search across reference numbers and debtor name
• **WaitingOn** - Client or Debitura (held cases only; string values)

Held cases expose hold.waitingOn, waitingSince, requestDelivery (Pending/Delivered/Failed) and the latest unanswered client question. Delivery failure reasons and internal reviewer data are never returned. Pending/failed delivery does not start a response deadline; an unknown delivery time stays null. Summary counts follow all requested filters, before pagination.

Sorting
• **Sort** - Sort field and direction (format: 'field:direction')
• Supported fields: DateCreated, GrossAmount, DueDate
• Examples: 'DateCreated:desc', 'GrossAmount:asc', 'DueDate:desc'
• Default: DateCreated:desc (newest first)

### Query parameters

- `FromDate` (datetime): Filter cases from this date (inclusive, based on case creation date). ISO 8601 format.
- `ToDate` (datetime): Filter cases to this date (inclusive, based on case creation date). ISO 8601 format.
- `ExternalTenantId` (string): Filter by specific client's external tenant ID
- `Statuses` (array of strings): Filter by case lifecycle status(es). Multiple values can be provided.
Accepts enum names (e.g. "Active", "Closed") or descriptions (e.g. "More Info Required").
- `Query` (string): Search query across case reference, creditor reference, and debtor name
- `WaitingOn` (string): Filter held cases by Client or Debitura. Numeric values are not accepted.
- `Page` (integer): Page number (1-indexed)
  Min 1.
- `PageSize` (integer): Number of results per page (default: 10, max: 100)
  1 to 100.
- `CountryIds` (array of integers): Filter by debtor country ID(s). Multiple values can be provided.
Only cases where the debtor's country matches one of the specified IDs are returned.
- `CollectionPartnerIds` (array of uuids): Filter by collection partner ID(s). Multiple values can be provided.
Only cases assigned to one of the specified collection partners are returned.
- `Sort` (string): Sort field and direction. Format: "field[:asc|desc]"
Supported fields: DateCreated, GrossAmount, DueDate
Examples: "DateCreated:desc", "GrossAmount:asc"
Default: DateCreated:desc

### Response 200

Cases retrieved successfully

- `page` (object): Paging metadata describing a paged result set.
- `page.totalResults` (integer): Total number of records for the query (filtered or all as applicable).
- `page.pageSize` (integer): How many records are returned per page.
- `page.currentPage` (integer): Which page number is being shown, calculated from the number of skipped items.
- `page.responseCount` (integer): How many records are present in the current page.
- `page.totalPages` (integer): Total number of pages available given TotalResults and PageSize.
Returns 0 if PageSize is 0 rather than dividing by zero.
- `cases` (array of objects, nullable): A debt collection case as seen by a referral partner, with your ExternalTenantId on Client.
- `cases[].id` (uuid): 
- `cases[].reference` (string, nullable): 
- `cases[].creditorReference` (string, nullable): 
- `cases[].client` (object): Client (creditor) info with referral-partner-specific ExternalTenantId.
- `cases[].client.externalTenantId` (string, nullable): Your external tenant ID for this client (the identifier you provided when linking).
- `cases[].client.creditorId` (uuid): 
- `cases[].client.companyName` (string, nullable): 
- `cases[].client.contacts` (object): Contact information for a client, including the original onboarding user,
the company office email, and all other users linked to the creditor.
- `cases[].client.contacts.onboardingUser` (object): A user contact associated with a creditor.
- `cases[].client.contacts.onboardingUser.email` (string, nullable): 
- `cases[].client.contacts.onboardingUser.name` (string, nullable): 
- `cases[].client.contacts.officeEmail` (string, nullable): The creditor's office email address (company-level contact).
- `cases[].client.contacts.otherUsers` (array of objects, nullable): Other users linked to this creditor account (excludes the onboarding user).
- `cases[].client.contacts.otherUsers[].email` (string, nullable): 
- `cases[].client.contacts.otherUsers[].name` (string, nullable): 
- `cases[].debtor` (object): Debtor info for the case.
- `cases[].debtor.name` (string, nullable): Debtor display name (company name, or person name if individual).
- `cases[].debtor.country` (string, nullable): Debtor's country (full name, e.g. "United Kingdom", "Denmark").
- `cases[].grossAmount` (number): 
- `cases[].remainder` (number): 
- `cases[].currency` (string, nullable): 
- `cases[].lifecycle` (string, nullable): Lifecycle status as human-readable description (e.g. "Active", "Closed", "Pending Verification").
- `cases[].closeCode` (string, nullable): Close code description if the case is closed (e.g. "Paid", "Withdrawn by Client"), null otherwise.
- `cases[].currentEngagementPhase` (string, nullable): The current phase of the case's engagement: "Pre-legal", "Legal", or "Enforcement". A
different axis from Lifecycle/CloseCode — an Active case can be
in any of the three phases.
            
Null means "no active engagement" (e.g. lead / quoting / pre-contract-signing / unassigned,
or a data-consistency gap) — a distinct third state, NOT a synonym for Pre-legal. Most cases
legitimately read Pre-legal; phase only leaves Pre-legal on legal/enforcement quote flows.
            
Not guaranteed to be monotonic (an admin correction can move phase backwards). Persists
after case closure — reflects the case's last-known engagement phase, not the current
Lifecycle.
- `cases[].dateCreated` (datetime): 
- `cases[].dueDate` (datetime): 
- `cases[].dateCollectionStarted` (datetime, nullable): 
- `cases[].dateFinished` (datetime, nullable): 
- `cases[].validation` (object): The case's current validation status: whether information is needed, and what to supply.
- `cases[].validation.needsInfo` (boolean): True when the latest validation run requires creditor action.
- `cases[].validation.creditorRespondedAt` (datetime, nullable): When the creditor (or managing partner) last explicitly responded to a needs-info request.
Null when the creditor has not yet responded, or after the case passes validation.
- `cases[].validation.items` (array of objects, nullable): Creditor-facing action items from the latest validation run.
Empty when NeedsInfo is false or no run exists.
- `cases[].validation.items[].resolutionArea` (string, nullable): Name of the resolution area (e.g. "ProvideDocuments", "CorrectDebtorAddress").
- `cases[].validation.items[].description` (string, nullable): Description of what needs to be fixed.
- `cases[].hold` (object): Allowlisted referral visibility; never attach validation entities or internal diagnostics.
- `cases[].hold.waitingOn` (enum): 
  Values: `Client`, `Debitura`.
- `cases[].hold.waitingSince` (datetime, nullable): 
- `cases[].hold.requestDelivery` (enum, nullable): 
  Values: `Pending`, `Delivered`, `Failed`, `null`.
- `cases[].hold.question` (string, nullable): 
- `cases[].collectionPartner` (string, nullable): Display name of the collection partner handling this case (e.g. "Oriel Collections").
Null if no collection partner has been assigned yet.
- `cases[].expectedReferralRatePercentage` (number, nullable): The expected referral fee rate as a percentage (e.g. 70.0 = 70%).
For cases with a collection partner that has a per-assignment override, the override is used.
Otherwise falls back to the referral link's snapshotted rate (frozen at link creation time).
Null when no collection partner has been assigned yet — a rate can only be determined
once routing is complete.
For realised payouts with frozen rates, see `referralFeePercentageSnapshot` on the
transactions endpoint.
- `summary` (object): 
- `summary.totalCases` (integer): 
- `summary.casesThisMonth` (integer): 
- `summary.byLifecycle` (object): 
- `summary.byLifecycle.active` (integer): 
- `summary.byLifecycle.pending` (integer): 
- `summary.byLifecycle.closed` (integer): 
- `summary.byLifecycle.paused` (integer): 
- `summary.byWaitingOn` (object): 
- `summary.byWaitingOn.client` (integer): 
- `summary.byWaitingOn.debitura` (integer): 

```json
{
  "page": {
    "totalResults": 42,
    "pageSize": 25,
    "currentPage": 1,
    "responseCount": 42,
    "totalPages": 1
  },
  "cases": [
    {
      "id": "3f2b6c1e-8a4d-4b7f-9c2e-5d1a0b9e7f31",
      "reference": "INV-2026-0042",
      "creditorReference": "INV-2026-0042",
      "client": {
        "externalTenantId": "string",
        "creditorId": "3f2b6c1e-8a4d-4b7f-9c2e-5d1a0b9e7f31",
        "companyName": "Muster GmbH",
        "contacts": {
          "onboardingUser": {},
          "officeEmail": {},
          "otherUsers": []
        }
      },
      "debtor": {
        "name": "Muster GmbH",
        "country": "Germany"
      },
      "grossAmount": 2500,
      "remainder": 1.5,
      "currency": "EUR",
      "lifecycle": "string",
      "closeCode": "string",
      "currentEngagementPhase": "string",
      "dateCreated": "2026-10-01T14:05:00Z",
      "dueDate": "2026-10-01T14:05:00Z",
      "dateCollectionStarted": "2026-10-01T14:05:00Z",
      "dateFinished": "2026-10-01T14:05:00Z",
      "validation": {
        "needsInfo": true,
        "creditorRespondedAt": "2026-10-01T14:05:00Z",
        "items": [
          {}
        ]
      },
      "hold": {
        "waitingOn": "Client",
        "waitingSince": "2026-10-01T14:05:00Z",
        "requestDelivery": "Pending",
        "question": "string"
      },
      "collectionPartner": "string",
      "expectedReferralRatePercentage": 1.5
    }
  ],
  "summary": {
    "totalCases": 42,
    "casesThisMonth": 3,
    "byLifecycle": {
      "active": 3,
      "pending": 3,
      "closed": 3,
      "paused": 3
    },
    "byWaitingOn": {
      "client": 3,
      "debitura": 3
    }
  }
}
```

### Response 400

Invalid request parameters

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 500

Internal server error

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

<a id="get-case-detail-with-potential-revenue-breakdown"></a>

## Get case detail with potential revenue breakdown

`GET /cases/{id}`

Retrieves a single case with all standard fields plus a potential revenue breakdown showing what the referral partner would earn if the full amount is collected.

### Path parameters

- `id` (uuid, required): 

### Response 200

Case retrieved successfully

- `id` (uuid): 
- `reference` (string, nullable): 
- `creditorReference` (string, nullable): 
- `client` (object): Client (creditor) info with referral-partner-specific ExternalTenantId.
- `client.externalTenantId` (string, nullable): Your external tenant ID for this client (the identifier you provided when linking).
- `client.creditorId` (uuid): 
- `client.companyName` (string, nullable): 
- `client.contacts` (object): Contact information for a client, including the original onboarding user,
the company office email, and all other users linked to the creditor.
- `client.contacts.onboardingUser` (object): A user contact associated with a creditor.
- `client.contacts.onboardingUser.email` (string, nullable): 
- `client.contacts.onboardingUser.name` (string, nullable): 
- `client.contacts.officeEmail` (string, nullable): The creditor's office email address (company-level contact).
- `client.contacts.otherUsers` (array of objects, nullable): Other users linked to this creditor account (excludes the onboarding user).
- `client.contacts.otherUsers[].email` (string, nullable): 
- `client.contacts.otherUsers[].name` (string, nullable): 
- `debtor` (object): Debtor info for the case.
- `debtor.name` (string, nullable): Debtor display name (company name, or person name if individual).
- `debtor.country` (string, nullable): Debtor's country (full name, e.g. "United Kingdom", "Denmark").
- `grossAmount` (number): 
- `remainder` (number): 
- `currency` (string, nullable): 
- `lifecycle` (string, nullable): 
- `closeCode` (string, nullable): 
- `currentEngagementPhase` (string, nullable): The current phase of the case's engagement: "Pre-legal", "Legal", or "Enforcement". Null
means no active engagement — see CurrentEngagementPhase for full
semantics (not monotonic, persists after case closure).
- `dateCreated` (datetime): 
- `dueDate` (datetime): 
- `dateCollectionStarted` (datetime, nullable): 
- `dateFinished` (datetime, nullable): 
- `validation` (object): The case's current validation status: whether information is needed, and what to supply.
- `validation.needsInfo` (boolean): True when the latest validation run requires creditor action.
- `validation.creditorRespondedAt` (datetime, nullable): When the creditor (or managing partner) last explicitly responded to a needs-info request.
Null when the creditor has not yet responded, or after the case passes validation.
- `validation.items` (array of objects, nullable): Creditor-facing action items from the latest validation run.
Empty when NeedsInfo is false or no run exists.
- `validation.items[].resolutionArea` (string, nullable): Name of the resolution area (e.g. "ProvideDocuments", "CorrectDebtorAddress").
- `validation.items[].description` (string, nullable): Description of what needs to be fixed.
- `hold` (object): Allowlisted referral visibility; never attach validation entities or internal diagnostics.
- `hold.waitingOn` (enum): 
  Values: `Client`, `Debitura`.
- `hold.waitingSince` (datetime, nullable): 
- `hold.requestDelivery` (enum, nullable): 
  Values: `Pending`, `Delivered`, `Failed`, `null`.
- `hold.question` (string, nullable): 
- `collectionPartner` (string, nullable): Display name of the collection partner handling this case (e.g. "Oriel Collections").
Null if no collection partner has been assigned yet.
- `expectedReferralRatePercentage` (number, nullable): The expected referral fee rate as a percentage (e.g. 70.0 = 70%).
Uses the per-assignment override when set, otherwise the referral link's snapshotted rate
(frozen at link creation time). Null when no collection partner has been assigned yet.
- `potentialRevenue` (object): Breakdown of potential revenue if the full case amount is collected.
All amounts are in USD; percentages are displayed as whole numbers (e.g. 9.5 = 9.5%).
- `potentialRevenue.grossAmountUsd` (number): Case gross amount converted to USD.
- `potentialRevenue.successFeePercentage` (number): Success fee percentage charged by the collection partner (e.g. 9.5).
- `potentialRevenue.collectionPartnerRevenueUsd` (number): GrossAmountUsd * SuccessFeePercentage / 100.
- `potentialRevenue.platformRevenueSharePercentage` (number): Debitura's revenue share percentage of the collection fee (e.g. 40.0).
- `potentialRevenue.platformRevenueUsd` (number): CollectionPartnerRevenueUsd * PlatformRevenueSharePercentage / 100.
- `potentialRevenue.referralFeePercentage` (number): Referral partner's share percentage of platform revenue (e.g. 50.0).
- `potentialRevenue.referralRevenueUsd` (number): PlatformRevenueUsd * ReferralFeePercentage / 100.

```json
{
  "id": "3f2b6c1e-8a4d-4b7f-9c2e-5d1a0b9e7f31",
  "reference": "INV-2026-0042",
  "creditorReference": "INV-2026-0042",
  "client": {
    "externalTenantId": "string",
    "creditorId": "3f2b6c1e-8a4d-4b7f-9c2e-5d1a0b9e7f31",
    "companyName": "Muster GmbH",
    "contacts": {
      "onboardingUser": {
        "email": "finance@example.com",
        "name": "Muster GmbH"
      },
      "officeEmail": "finance@example.com",
      "otherUsers": [
        {
          "email": {},
          "name": {}
        }
      ]
    }
  },
  "debtor": {
    "name": "Muster GmbH",
    "country": "Germany"
  },
  "grossAmount": 2500,
  "remainder": 1.5,
  "currency": "EUR",
  "lifecycle": "string",
  "closeCode": "string",
  "currentEngagementPhase": "string",
  "dateCreated": "2026-10-01T14:05:00Z",
  "dueDate": "2026-10-01T14:05:00Z",
  "dateCollectionStarted": "2026-10-01T14:05:00Z",
  "dateFinished": "2026-10-01T14:05:00Z",
  "validation": {
    "needsInfo": true,
    "creditorRespondedAt": "2026-10-01T14:05:00Z",
    "items": [
      {
        "resolutionArea": "string",
        "description": "string"
      }
    ]
  },
  "hold": {
    "waitingOn": "Client",
    "waitingSince": "2026-10-01T14:05:00Z",
    "requestDelivery": "Pending",
    "question": "string"
  },
  "collectionPartner": "string",
  "expectedReferralRatePercentage": 1.5,
  "potentialRevenue": {
    "grossAmountUsd": 2500,
    "successFeePercentage": 2500,
    "collectionPartnerRevenueUsd": 1.5,
    "platformRevenueSharePercentage": 1.5,
    "platformRevenueUsd": 1.5,
    "referralFeePercentage": 2500,
    "referralRevenueUsd": 1.5
  }
}
```

### Response 404

Case not found

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 500

Internal server error

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

<a id="upload-a-file-to-a-case-optionally-posts-a-system-chat-message-t"></a>

## Upload a file to a case. Optionally posts a system chat message to notify the collection partner.

`POST /cases/{caseId}/files`

Uploads a file and associates it with a case owned by one of the referral partner's clients.

File requirements:
- Maximum file size: 25 MB
- Allowed types: PDF, Excel (.xls, .xlsx), CSV, Text (.txt), Images (.jpg, .jpeg, .png, .gif)

documentType (optional form field, case-insensitive) says what the document is. Omit it when you do not know it;
the file is then stored as not specified. Valid values: OriginalInvoice, AccountStatement, Contract, TermsAndConditions,
ProofOfDelivery, DemandLetter, Correspondence, PaymentProof, CourtDocument, IdentityDocument, Miscellaneous.
Still accepted for existing integrations: DebtorDocuments, CreditorDocuments, PartnerDocuments.
Any other value is rejected with 400 and error InvalidDocumentType.

When sendChatNotification is true (default), a system message is posted on the case chat
to notify the collection partner that a new file was uploaded.

### Path parameters

- `caseId` (uuid, required): 

### Query parameters

- `sendChatNotification` (boolean): 

### Headers

- `Idempotency-Key` (string): Optional non-empty, non-whitespace idempotency key (max 255 characters) for safely retrying this request. If a previous request used the same key with an identical body, the original terminal response is replayed verbatim. Reusing the key with a different body returns 422 with `businessErrors[0].type: "IdempotencyConflict"`. Field-level 400 validation errors are not stored, so you may fix the request and retry with the same key.
  Max 255 characters.

### Request body (multipart/form-data)

- `file` (file): 
- `documentType` (string): 

### Response 200

File uploaded successfully

### Response 400

Validation error

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 404

Case not found

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 413

File too large

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 415

Unsupported file type

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 500

Internal server error

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

<a id="preview-case-pricing"></a>

## Preview case pricing

`POST /cases/preview`

Preview the success fee pricing for a potential case without creating it.

**What This Endpoint Returns:**
- IsEligible - Whether the case can be submitted (partner available in this jurisdiction)
- PricingPreview - Estimated success fee with base rate and any age-based surcharges
- Jurisdiction - Geographic details for the matched jurisdiction

**Use This To:**
1. Show your clients the Debitura success fee before they onboard
2. Check if a jurisdiction is supported before starting the onboarding flow
3. Display age-adjusted pricing for aged debt portfolios

**Required Fields:**
- AmountToRecover - Total principal amount
- CurrencyCode - ISO 4217 currency code (e.g., 'GBP', 'EUR', 'USD')
- Debtor.Type - 'Company' or 'Private'
- Debtor.CountryAlpha2 - ISO 3166-1 alpha-2 country code
- Debtor.StateAlpha2 - Required for US cases only (e.g., 'CA', 'NY')

**Age-Based Pricing (Optional)**

**Single invoice — pass `dueDate`:** The simplest way to get age-adjusted pricing. Provide the invoice due date (ISO 8601, e.g. `2024-01-15`) and Debitura computes the age surcharge internally. Cannot be a future date. Mutually exclusive with age bucket fields.

**Multi-invoice portfolio — pass age buckets:** For portfolios with invoices at different ages, pass the portion of the total that falls into each age bracket:
- `AmountToRecoverOver12Months` and `AmountToRecoverOver24Months` must be provided together
- `AmountToRecoverOver6Months` is optional but improves pricing tier accuracy at the 180-day threshold; can only be provided alongside the 12/24-month fields, must be >= `AmountToRecoverOver12Months`, and must be <= `AmountToRecover`

**Multi-invoice portfolio — pass `claimLines` instead (recommended):** Send the unpaid invoices and let Debitura do the arithmetic:

```
"claimLines": [
  { "dueDate": "2024-01-15", "amount": 423.42, "reference": "INV-1001" },
  { "dueDate": "2025-05-02", "amount": 700.87, "reference": "INV-1042" }
]
```

- `amount` is the **outstanding balance** on that invoice, already net of payments and credit notes — not its original face value.
- Debitura derives `amountToRecover` and all three age buckets from the lines and prices on them, so send `claimLines` **instead of** `amountToRecover`, the age bucket fields, and `dueDate`. Combining any of those with `claimLines` is a 400.
- Use `claimLines` whenever the debtor has already part-paid: it is the only way to express an honest age profile on a part-paid claim, because the buckets are computed on the same outstanding balances the total is.
- Maximum 1000 lines. Per-line errors are returned keyed by index, e.g. `ClaimLines[2].DueDate`.
- The same ledger produces the same derived total and buckets on POST /clients case submission.

`dueDate`, the age bucket fields and `claimLines` are mutually exclusive — use one of the three. When no age fields are provided, base pricing is returned without age surcharge.

**Important Notes:**
- This endpoint does NOT create a case — it only previews pricing
- Pricing is based on the referral partner's agreed rate (jurisdiction pricing zone rate if applicable)
- No creditor or client account is needed

### Request body (application/json)

- `amountToRecover` (number): The TOTAL amount to recover for this case.
This represents the full principal amount across all invoices (if multiple invoices are bundled).
            
For multi-invoice cases with different ages, you can optionally provide age breakdown fields
(AmountToRecoverOver12Months and AmountToRecoverOver24Months) to enable blended age-based pricing.
            
If age breakdown fields are omitted, the preview will show base pricing without age surcharge calculation.
            
Required unless you send ClaimLines, in which case this must be omitted or 0 — the server
derives the total by summing the outstanding balance of every claim line. Sending both a
non-zero AmountToRecover and ClaimLines is rejected, because the two would disagree.
- `currencyCode` (string, required): ISO 4217 currency code for the amount (e.g., "DKK", "EUR", "USD")
  3–3 characters.
- `debtor` (object, required): Simplified debtor information for case preview.
Only includes the minimum fields required for partner routing and eligibility checking.
- `debtor.type` (string, required): Debtor type. Valid values: "Company" or "Private"
This affects partner selection as different partners may specialize in B2B vs B2C collection.
- `debtor.countryAlpha2` (string, required): Country code (ISO 3166-1 alpha-2 format, e.g., "DK", "US", "PL")
Used to determine jurisdiction and available collection partners.
  2–2 characters.
- `debtor.stateAlpha2` (string, nullable): US state code (e.g., "CA", "NY")
REQUIRED for United States cases only, as US partner coverage is state-specific.
Optional for other countries.
  2–2 characters.
- `amountToRecoverOver6Months` (number, nullable): OPTIONAL - MULTI-INVOICE AGE BUCKET PRICING (PRICING ZONE 180-DAY THRESHOLD)
            
The portion of AmountToRecover that is more than 6 months (180 days) overdue.
This includes amounts that are 12+ and 24+ months overdue.
            
This field is optional and can only be provided alongside AmountToRecoverOver12Months
and AmountToRecoverOver24Months. It enables precise pricing tier selection based on
the 180-day threshold. When omitted, the system falls back to deriving the threshold
from AmountToRecoverOver12Months.
            
Validation rules:
- Must be ≤ AmountToRecover
- Must be ≥ AmountToRecoverOver12Months
- `amountToRecoverOver12Months` (number, nullable): OPTIONAL - MULTI-INVOICE AGE BUCKET PRICING
            
The portion of AmountToRecover that is more than 12 months overdue.
This includes amounts that are 24+ months overdue.
            
If provided, AmountToRecoverOver24Months must also be provided.
Both fields must be provided together or both omitted.
            
Used to calculate blended age uplift for multi-invoice cases:
- Amount under 12 months = AmountToRecover - AmountToRecoverOver12Months
- Amount 12-24 months = AmountToRecoverOver12Months - AmountToRecoverOver24Months
- Amount over 24 months = AmountToRecoverOver24Months
            
Blended uplift formula: ((A12-A24)×10 + A24×20) / AmountToRecover
where A12 = AmountToRecoverOver12Months, A24 = AmountToRecoverOver24Months
            
Validation rules:
- Must be ≤ AmountToRecover
- Must be ≥ AmountToRecoverOver24Months
- `amountToRecoverOver24Months` (number, nullable): OPTIONAL - MULTI-INVOICE AGE BUCKET PRICING
            
The portion of AmountToRecover that is more than 24 months overdue.
            
If provided, AmountToRecoverOver12Months must also be provided.
Both fields must be provided together or both omitted.
            
Used to calculate blended age uplift for multi-invoice cases.
See AmountToRecoverOver12Months for full details on the calculation.
            
Validation rules:
- Must be ≤ AmountToRecoverOver12Months
- Must be ≤ AmountToRecover
- `claimLines` (array of objects, nullable): OPTIONAL - the unpaid invoices making up this claim, one line per invoice, each carrying its
own outstanding balance and payment deadline.
            
When supplied, the server derives AmountToRecover and all three cumulative age buckets from
these lines and prices on them, so send them instead of — never alongside — AmountToRecover,
the AmountToRecoverOver6/12/24Months fields, or DueDate. Supplying any of those together
with ClaimLines is rejected with a 400.
            
Use this whenever the debtor has already part-paid the claim: the buckets are then computed
on the same outstanding balances the total is computed on, so the age profile stays honest.
Maximum 1000 lines. Claim lines are a pricing input only and are never stored.
- `claimLines[].dueDate` (datetime, required, nullable): Payment deadline of this invoice. Provide date only without time component.
Must be on or after 2000-01-01 and must not be in the future.
- `claimLines[].amount` (number, required): The outstanding balance on this invoice in the case currency — what the debtor still owes on it,
already net of any payments the debtor has made and net of any credit notes issued against it.
This is not the invoice's original face value.
Must be greater than zero with at most 2 decimal places; omit invoices that are fully settled.
- `claimLines[].reference` (string, nullable): Optional invoice number, used for validation and duplicate-line detection. Starter retains it with the original-line case snapshot, visible through existing case permissions; Full Integration does not retain that snapshot.
- `dueDate` (datetime, nullable): Optional invoice due date. Mutually exclusive with age bucket fields and with ClaimLines.
If provided, Debitura computes the age surcharge internally — no need to calculate age buckets.
Cannot be a future date.
- `returnUrl` (string, nullable): Optional: URL the creditor user should land on after completing any
pending signing chain (SDCA upgrade / PoA / JPA / KYC) on the
Debitura Creditors app. Embedded, URL-encoded, into
`PreviewCaseResponseApiViewModel.Result.SigningHandoff.CombinedSigningUrl`
(when at least one signing-related required action is present) and into
`BusinessErrorResponseApiDTO.SigningHandoff.CombinedSigningUrl` /
`InvoiceDto.SigningHandoff.CombinedSigningUrl` on POST `/cases`.
            
Must be an absolute http(s) URL. Values that fail validation
(relative URLs, non-http schemes, header-injection characters) are
silently dropped — the combined URL is still emitted but without
the returnUrl query parameter, and the Creditors app falls back to
its own safe in-app default landing.
            
Additive. Existing integrations
that omit this field see the same behaviour as before.
  Max 2048 characters.

```json
{
  "amountToRecover": 4000,
  "currencyCode": "EUR",
  "debtor": {
    "type": "Company",
    "countryAlpha2": "PT"
  },
  "dueDate": "2026-05-01"
}
```

### Response 200

Preview completed

- `isEligible` (boolean): Whether the case is eligible for collection (a partner is available in the requested jurisdiction).
If false, check IneligibilityReason for details.
- `ineligibilityReason` (string, nullable): Explanation of why the case is not eligible (only present when IsEligible = false).
Branch on IneligibilityReasonCode rather than on this text, which may be reworded.
- `ineligibilityReasonCode` (string, nullable): Stable machine-readable reason, present whenever IneligibilityReason is
One of: NoGeographicCoverage, AmountBelowMinimum, AmountAboveMaximum,
DebtorTypeNotCovered, NotCoveredByPartnerRules, ExcludedForClient, EligibilityUndetermined.

Only NoGeographicCoverage means we have no partner in the jurisdiction. AmountBelowMinimum
means the geography IS covered and only the claim amount fell short — do not advise the client
to correct their address. EligibilityUndetermined is a transient failure on our side.
- `applicableMinimumAmount` (number, nullable): What this claim would have to reach to be accepted, in the currency of the request; never below
the platform minimum of 100 USD. Only present when no partner was matched, and not when the request
currency cannot be priced. Null when no partner covering this case declares a minimum.
- `applicableMinimumCurrencyCode` (string, nullable): ISO code that ApplicableMinimumAmount is expressed in.
- `marketMinimumAmount` (number, nullable): The smallest claim the marketplace takes for this jurisdiction and debtor type, in the currency of
the request: the lowest partner floor, never below the platform minimum of 100 USD. A statement
about the market, not about this claim: it is returned whether or not a partner was selected, and
does not depend on the claim amount or on the caller. It is not the admission rules applied when a
case is created. Null when no partner covers the market or the amount cannot be priced.
- `marketMinimumCurrencyCode` (string, nullable): ISO code that MarketMinimumAmount is expressed in.
- `amountCheckUnavailable` (boolean): True when a currency conversion needed to evaluate the claim amount was unavailable, so the
amount-based part of this answer was NOT actually checked It can be true on an
eligible answer: a partner matched only because amount conditions failed open while rates were
down. Treat such an answer as provisional and retry rather than relying on the floor having
been applied.
- `jurisdiction` (object): Geographic jurisdiction information for the case.
- `jurisdiction.id` (integer): Internal jurisdiction identifier.
- `jurisdiction.name` (string, nullable): Display name of the jurisdiction (e.g., "Denmark", "United States - California").
- `jurisdiction.countryAlpha2` (string, nullable): ISO 3166-1 alpha-2 country code.
- `jurisdiction.stateAlpha2` (string, nullable): ISO 3166-2 state/region code (only for countries with state-level routing like the US).
- `pricingPreview` (object): Estimated pricing for the case, including age-based surcharges.
- `pricingPreview.baseSuccessFeePercentage` (number): Base success fee percentage before age surcharges.
This is the standard rate for cases under 12 months overdue.
- `pricingPreview.ageSurchargePercentage` (number): Additional percentage points added for aged invoices.
Calculated based on invoice age:
- 12-24 months overdue: +8 percentage points
- 24+ months overdue: +15 percentage points
- Multi-invoice with mixed ages: blended rate (0-15 percentage points)
- `pricingPreview.totalSuccessFeePercentage` (number): Total success fee percentage (base + age surcharge).
This is the percentage of the recovered amount that will be charged as a success fee.
- `pricingPreview.currencyCode` (string, nullable): ISO 4217 currency code for the case.
- `pricingPreview.pricingSource` (string, nullable): Which pricing rule determined the fee.
Possible values: "Standard agreement", "Creditor custom pricing",
"Managing partner custom pricing", "Jurisdiction custom pricing",
"Partner custom pricing", "Accepted quote".
- `pricingPreview.pricingSourceDescription` (string, nullable): Human-readable explanation of why this pricing was applied.
Example: "9.5% success fee — BD Legal custom collection rates (EU zone, $1,000–$7,999 tier)"
- `requiresKycVerification` (boolean): Whether the resolved collection partner requires KYC verification before cases can be submitted.
When true, the creditor must complete KYC verification before case creation.
Use the case creation endpoint with a creditor ID to determine whether KYC is already on file.
- `pendingActions` (array of objects, nullable): Structured list of actions the creditor will need to complete before
(or as part of) submitting cases for the matched collection partner.
Always present — empty when nothing is pending.
            
At preview time the request is creditor-less, so this list reflects the
matched partner's hard requirements (e.g. PoA per partner, KYC if the
partner is configured for it). Creditor-specific items such as SDCA
upgrade are returned only when the API has creditor context.
            
Additive over RequiresKycVerification — old clients that
only read RequiresKycVerification are unaffected.
- `pendingActions[].type` (enum): Type of action that may need to be completed by the creditor before
(or as part of) submitting cases via the referral partner API.
New values may be added in future API versions — clients should treat
unknown values as informational and not crash.
            
Sent as named string values (e.g. `"PoA"`, `"Kyc"`). Consume the string
names; the numbers are not part of the public contract.
  Values: `SdcaUpgrade`, `PoA`, `Jpa`, `Kyc`.
- `pendingActions[].collectionPartnerId` (uuid, nullable): The collection partner this action is scoped to, when applicable.
Set for partner-specific actions (PoA, KYC). Null for actions that
are not partner-scoped (e.g. SDCA upgrade, which is platform-wide).
- `pendingActions[].dueDate` (datetime, nullable): Optional deadline by which the action must be completed (UTC).
Always null on the `/cases/preview` endpoint.
- `combinedSigningUrl` (string, nullable): Always `null` on `/cases/preview` because preview is creditor-less —
no concrete creditor signing state exists to chain. The structured
PendingActions array on this response indicates which step types
the matched partner will require ahead of onboarding.
            
The canonical placement for the combined-signing URL on POST `/cases`
responses is the nested `SigningHandoff` envelope (a
`SigningHandoffDto` on both `BusinessErrorResponseApiDTO` and
`InvoiceDto`). The combined URL is populated on POST `/cases`
responses, where the creditor IS identified:
<list type="bullet"><item><description>422 with at least one signing-related business error
  (`MissingDebtCollectionContract`, `MissingPowerOfAttorney`,
  `MissingKycVerification`) — see
  `BusinessErrorResponseApiDTO.SigningHandoff.CombinedSigningUrl`.</description></item><item><description>200 with `AllowPendingContracts=true` when signings
  remain — see `InvoiceDto.SigningHandoff.CombinedSigningUrl`.</description></item></list>
Both POST surfaces use the same Creditors-app
`/Signing/PendingContracts` entry point and walk the SDCA-upgrade →
PoA → JPA → KYC chain end-to-end.
            
This flat `CombinedSigningUrl` field on `/cases/preview` is
preserved as-is for backward compatibility (it is always null in practice).

```json
{
  "isEligible": true,
  "ineligibilityReason": null,
  "ineligibilityReasonCode": null,
  "applicableMinimumAmount": 2500,
  "applicableMinimumCurrencyCode": "EUR",
  "marketMinimumAmount": 2500,
  "marketMinimumCurrencyCode": "EUR",
  "amountCheckUnavailable": true,
  "jurisdiction": {
    "id": 3,
    "name": "Muster GmbH",
    "countryAlpha2": "DE",
    "stateAlpha2": null
  },
  "pricingPreview": {
    "baseSuccessFeePercentage": 2500,
    "ageSurchargePercentage": 1.5,
    "totalSuccessFeePercentage": 2500,
    "currencyCode": "EUR",
    "pricingSource": "string",
    "pricingSourceDescription": "string"
  },
  "requiresKycVerification": true,
  "pendingActions": [
    {
      "type": "SdcaUpgrade",
      "collectionPartnerId": "3f2b6c1e-8a4d-4b7f-9c2e-5d1a0b9e7f31",
      "dueDate": "2026-10-01T14:05:00Z"
    }
  ],
  "combinedSigningUrl": "https://app.debitura.com/…"
}
```

### Response 400

Invalid request (validation errors)

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```

### Response 500

Internal server error

- `error` (string, nullable): Short human-readable error label (legacy field — preserved for backward compatibility).
- `message` (string, nullable): Human-readable error description (legacy field — preserved for backward compatibility).
- `businessErrors` (array of objects, nullable): Canonical structured error array. Consumers should migrate to reading this field.
- `businessErrors[].type` (string, nullable): The type of business error (e.g., "MissingDebtCollectionContract", "MissingPowerOfAttorney", "NoPartnerAvailable").
- `businessErrors[].message` (string, nullable): A human-readable description of the error.
- `businessErrors[].solutionUrl` (string, nullable): A URL where the user can resolve this <i>specific</i> error (e.g., sign a contract).
            

Partners who want a <b>single URL</b> that walks the user through every pending
signing in one chain — instead of forwarding one URL per
`BusinessErrorApiDTO` — should use
SigningHandoff's `CombinedSigningUrl`.
The two are complementary: `SolutionUrl` is granular per-error;
`CombinedSigningUrl` is the chain-walking alternative for signing-related errors.

```json
{
  "error": null,
  "message": "Payment plan agreed with the debtor.",
  "businessErrors": [
    {
      "type": "string",
      "message": "Payment plan agreed with the debtor.",
      "solutionUrl": "https://app.debitura.com/…"
    }
  ]
}
```
