Source: https://docs.debitura.com/referral-partners/integration-guides/case-submission

# Case Submission

Submit debt collection cases for your linked clients using the Customer API.

Full Integration, and Starter clients too

This is the Full Integration's way to create cases. A Starter client can use it as well: mint a token with the same `externalTenantId`, with no new onboarding. The [Starter Integration](https://docs.debitura.com/referral-partners/integration-guides/starter-integration.md) itself creates cases through `POST /collection-requests`, with no token.

<a id="overview"></a>

## Overview

After [onboarding a client](https://docs.debitura.com/referral-partners/integration-guides/client-onboarding.md), you submit cases through the **Customer API** (not the Referral Partner API). This is the [two-API architecture](https://docs.debitura.com/referral-partners/getting-started/how-it-works.md) in action:

1.  **Mint a bearer token** via the Referral Partner API
2.  **Submit the case** via the Customer API using that token
3.  Revenue is automatically attributed to your partnership

<a id="step-1-mint-a-bearer-token"></a>

## Step 1: Mint a Bearer Token

```http
POST /oauth/token HTTP/1.1
Host: referral-api.debitura.com
XApiKey: YOUR_API_KEY
Content-Type: application/json

{
  "externalTenantId": "your-client-id"
}
```

```json
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 1800
}
```

The token is valid for 30 minutes and scoped to this specific client. See [Authentication](https://docs.debitura.com/referral-partners/getting-started/authentication.md) for caching and retry patterns.

Client must be onboarded

Token minting returns `404` if there is no active, non-archived client link for the supplied `externalTenantId` — typically because the client was never created or the link has been withdrawn/archived. A token is issued as soon as a non-deleted, non-archived link exists; it does **not** require the SDCA to be signed. Unsigned-contract handling happens later at case submission via `allowPendingContracts` (422 / `PendingContractSigning`), not at token minting.

<a id="step-2-submit-the-case"></a>

## Step 2: Submit the Case

Use the bearer token with the **Customer API** (different base URL):

```http
POST /cases HTTP/1.1
Host: customer-api.debitura.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "currencyCode": "EUR",
  "amountToRecover": 5000.00,
  "date": "2025-06-01",
  "dueDate": "2025-07-01",
  "claimDescription": "Invoice INV-2025-001 for consulting services",
  "debtor": {
    "type": "Company",
    "name": "Debtor GmbH",
    "contactPerson": "Hans Mueller",
    "address": "Hauptstrasse 10",
    "city": "Munich",
    "zipCode": "80331",
    "countryAlpha2": "DE",
    "email": "billing@debtor.de"
  }
}
```

The case is automatically attributed to your partnership because you used a bearer token minted through the Referral Partner API.

This example uses one amount and one due date. Before mapping production claims, compare the [three claim amount and age modes](https://docs.debitura.com/developer-concepts/claim-amount-and-age.md). Customer API case creation also accepts cumulative age buckets or `claimLines`.

For a multi-invoice case, send `claimLines` instead of `amountToRecover` and the age bucket fields:

```json
{
  "currencyCode": "EUR",
  "date": "2024-01-01",
  "claimLines": [
    { "dueDate": "2024-01-15", "amount": 423.42, "reference": "INV-1001" },
    { "dueDate": "2025-05-02", "amount": 700.87, "reference": "INV-1042" }
  ],
  "debtor": {
    "type": "Company",
    "name": "Debtor GmbH",
    "countryAlpha2": "DE"
  }
}
```

Each line carries the invoice's outstanding balance after payments and credit notes. Debitura derives the case total and cumulative age buckets. A case-level `dueDate` may coexist with `claimLines`; when omitted, it defaults to the oldest line due date. Claim lines are pricing input only and are not stored or shown to the collecting partner.

<a id="key-fields"></a>

### Key fields

| Field | Required | Notes |
| --- | --- | --- |
| `currencyCode` | Yes | ISO 4217 (EUR, GBP, USD, etc.) |
| `amountToRecover` | Conditional | Total principal amount. Omit when sending `claimLines` |
| `claimLines` | Conditional | 1–1,000 unpaid invoices. Use instead of `amountToRecover` and age bucket fields |
| `date` | Yes | Invoice date (must not be in the future) |
| `dueDate` | No | When payment was due. Must not be in the future (today is allowed) and not earlier than `date` |
| `debtor.type` | Yes | `"Company"` or `"Private"` |
| `debtor.name` | Yes | Legal name |
| `debtor.countryAlpha2` or `debtor.country` | Yes | ISO 3166-1 alpha-2 code (preferred) or country name |
| `debtor.stateAlpha2` or `debtor.state` | US only | State code (preferred, e.g. `"CA"`, `"NY"`) or full state name |
| `isTest` | No | Set `true` for test cases that won't trigger real collection |

For the complete field reference and debtor validation rules, see [Create a Case](https://docs.debitura.com/clients/integration-guides/create-a-case.md).

`isTest` vs `isTestCase`

You **send** `isTest` in the request body. The API **returns** `isTestCase` in the response. They represent the same flag -- the naming differs between request and response models.

<a id="handling-pending-signing-after-submission"></a>

### Handling Pending Signing After Submission

When the linked client has not yet signed the SDCA or another required contract and the request is sent with `allowPendingContracts: true`, the case is created in the `Pending contract signing` lifecycle and the response includes a `signingHandoff` object:

```json
{
  "lifecycle": "Pending contract signing",
  "signingHandoff": {
    "combinedSigningUrl": "https://app.debitura.com/Signing/PendingContracts?returnUrl=..."
  },
  ...
}
```

Use `signingHandoff.combinedSigningUrl` to render a signing prompt before the user can continue. The URL walks the full pending chain (SDCA upgrade → PoA → JPA → KYC) in one flow, so no client-side decision logic is needed. When everything is already signed, `signingHandoff` is `null`.

Pass `returnUrl` (absolute http(s) URL, max 2048 chars) on the `POST /cases` request to deep-link the user back to your app once signing is complete. Unsafe values within the 2048-character limit are silently dropped; longer values return `400`. With no usable URL, the receiving signing page uses its own default landing.

After a successful signing completion, Debitura appends `completed=true` to your returnUrl before redirecting (e.g. `https://yourapp.com/done?completed=true`). If the user abandons the flow without completing it, the redirect happens without this parameter. Use this to distinguish completed flows from abandoned ones.

For pricing previews before submission, see [Preview Case Pricing](https://docs.debitura.com/referral-partners/integration-guides/preview-case-pricing.md) — the preview endpoint reports which signing steps the matched partner will require via a `pendingActions` array.

<a id="submitting-cases-during-onboarding"></a>

## Submitting Cases During Onboarding

You can include cases directly in the `POST /clients` request. Debitura validates all cases upfront:

Cases embedded in this request follow the same [claim amount and age rules](https://docs.debitura.com/developer-concepts/claim-amount-and-age.md) as Customer API case creation and accept a single due date, cumulative age buckets, or `claimLines`. Each case accepts at most 1,000 lines, and the complete `POST /clients` request accepts at most 1,000 lines across all cases.

-   If all cases are valid and have matching collection partners, they're created alongside the client
-   Cases without available partners are reported in `CaseResults.FailedCases` (the client is still created)
-   If validation errors exist (missing fields, duplicate references), the entire request returns 400

This is useful for platforms where the user submits their first invoice at the same time as signing up.

See [Client Onboarding — Including Cases](https://docs.debitura.com/referral-partners/integration-guides/client-onboarding.md#including-cases) for details.

<a id="uploading-files-to-a-case"></a>

## Uploading Files to a Case

Upload supporting documents to an existing case with `POST /cases/{caseId}/files` on the Referral Partner API. The case must belong to one of your linked clients.

```http
POST https://referral-api.debitura.com/cases/{caseId}/files?sendChatNotification=true
XApiKey: YOUR_API_KEY
Content-Type: multipart/form-data

file: <binary>
documentType: AccountStatement
```

| Parameter | Type | Notes |
| --- | --- | --- |
| `file` | form-data | Max 25 MB. Allowed extensions: `.pdf`, `.doc`, `.docx`, `.xls`, `.xlsx`, `.csv`, `.txt`, `.jpg`, `.jpeg`, `.png`, `.zip`, `.eml` |
| `documentType` | form-data (string, optional) | Type of document; for example, `AccountStatement`. Omit to leave it Not specified |
| `sendChatNotification` | query (bool) | Defaults to `true`. When `true`, a system chat message (`"System message: New file uploaded under 'Files': <filename>"`) is posted to the case thread so the assigned collection partner sees the upload in-app. Set `false` for silent uploads |

Returns `200` on success with `{ id, fileName, dateCreated }`. Returns `404` if the case is not linked to one of your clients, `413` if the file exceeds 25 MB, and `415` for unsupported file types.

<a id="document-types"></a>

### Document Types

The optional `documentType` field describes what the file is. API names are case-insensitive and surrounding whitespace is ignored. Omit the field when you do not know the type: the file is stored as **Not specified**, returned as `documentType: null`. `Miscellaneous` explicitly means **Other**.

| API name | Use for |
| --- | --- |
| `OriginalInvoice` | Invoice or credit note |
| `AccountStatement` | Statement of account showing invoices, payments, and the outstanding balance |
| `Contract` | Contract or agreement |
| `TermsAndConditions` | Terms and conditions |
| `ProofOfDelivery` | Order or proof of delivery |
| `DemandLetter` | Reminder or demand letter |
| `Correspondence` | Correspondence with the debtor |
| `PaymentProof` | Proof of payment |
| `CourtDocument` | Court or legal document |
| `IdentityDocument` | Identity document |
| `Miscellaneous` | Other supporting documents |
| `DebtorDocuments` | Legacy: dispute documents from the debtor |
| `CreditorDocuments` | Legacy: dispute documents from the creditor |
| `PartnerDocuments` | Legacy: dispute documents from the collection partner |

The three legacy names remain accepted for existing integrations. Prefer the specific document types for new uploads.

Unknown names, numeric values, and `AutoGeneratedSummary` are rejected with `400` and error `InvalidDocumentType`. `AutoGeneratedSummary` is system-only and may appear in file-list responses.

A file's type can change after upload: admins can correct it, and an omitted type may later be filled automatically when automatic classification is enabled. Handle `null` on reads and do not assume it is permanent.

<a id="submitting-kyc-verification"></a>

## Submitting KYC Verification

Submit director identity data for a linked client with `POST /clients/{externalTenantId}/kyc-verification`. See [KYC Verification](https://docs.debitura.com/referral-partners/integration-guides/kyc-verification.md) for the payload. Returns `201 Created` with the saved KYC record, or `404` if the client is not linked to your partner account.

The endpoint supports an optional `?sendChatNotification=true` (default `true`) query flag. When enabled, a system chat message (`"System message: KYC/AML information provided — find it under the 'KYC Status' section"`) is posted on each of the client's cases that are **not yet closed** (`Active`, `Paused`, or `NeedsAdditionalDetails` lifecycle) **and** are assigned to a collection partner that requires KYC. Cases assigned to partners that don't require KYC are skipped, and closed cases are never touched. Pass `false` to submit silently.

<a id="tracking-cases"></a>

## Tracking Cases

After submission, track cases using either API:

| What you need | API | Endpoint |
| --- | --- | --- |
| All cases across all clients | Referral Partner | `GET /cases` (with API key) |
| Single case detail | Customer | `GET /cases/{id}` (with bearer token) |
| Real-time updates | Referral Partner | [Webhooks](https://docs.debitura.com/referral-partners/integration-guides/webhooks.md) (`case.created`, `case.updated`, `case.closed`) |

<a id="error-handling"></a>

## Error Handling

| Status | Common causes |
| --- | --- |
| `400` | Missing required fields, invalid debtor data |
| `401` | Expired bearer token — [mint a new one](https://docs.debitura.com/referral-partners/getting-started/authentication.md#token-expiry-handling) and retry |
| `422` | Business rule violation (e.g. duplicate creditor reference, `MissingKycVerification`, `MissingPowerOfAttorney`, unsupported jurisdiction) |

For `422` errors related to KYC, see [KYC Verification](https://docs.debitura.com/referral-partners/integration-guides/kyc-verification.md).

See [Create a Case](https://docs.debitura.com/clients/integration-guides/create-a-case.md) for the full Customer API field reference and validation rules. See [Preview Pricing](https://docs.debitura.com/referral-partners/integration-guides/preview-case-pricing.md) to check the success fee before submitting, [Webhooks](https://docs.debitura.com/referral-partners/integration-guides/webhooks.md) to get notified when case status changes, and [Revenue Tracking](https://docs.debitura.com/referral-partners/integration-guides/revenue-tracking.md) to track your earnings from submitted cases.
