> ## Documentation Index
> Fetch the complete documentation index at: https://creditor-documentation.debbiecollect.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Invoice-to-cash integration

> Hand unpaid invoices to your debt collection partner, and get the money and the bookkeeping back

This guide describes how to connect the system that issues your invoices — your
ERP, billing or subscription system — to your debt collection partner, so that
an unpaid invoice becomes a collection case without anyone retyping it, and the
money comes back with enough detail to be posted.

The integration is small. Four endpoints push data in, and one webhook plus two
endpoints bring the settlement back:

<Steps>
  <Step title="Create your customer">
    The debtor, with the contact details the collector will write to.
  </Step>

  <Step title="Add the case vouchers">
    The unpaid invoices, and any reminder and compensation fees you have already
    charged.
  </Step>

  <Step title="Attach the documentation">
    Invoice copies, contracts, photos — whatever proves the claim.
  </Step>

  <Step title="Register direct payments">
    Money the debtor pays straight to you after handover.
  </Step>

  <Step title="Book the settlement">
    What you are paid, and what you are charged.
  </Step>
</Steps>

## Before you start

1. **Get access to the creditor portal.** Your debt collection partner invites
   you. If you do not have access yet, reach out to them.
2. **Create an API key.** In the portal, go to *Developers → API*, name the key
   and grant it the scopes listed under [Scopes](#scopes). Authenticate with
   `Authorization: Bearer {token}` — see
   [Authentication](/api-reference/authentication).
3. **Note your ids.** The same page shows your `tenantId` — your debt collection
   partner's tenant, which is part of every URL — and your `creditorId`, which
   identifies you.
4. **Ask for the configuration ids you need.** The case group you should send
   cases under, the handles of any custom attributes, and the id of any custom
   voucher type. These are set up in the platform by your partner.
5. **Add a webhook.** Under *Developers → Webhooks* in the portal, or with
   [Create webhook](/api-reference/endpoints/webhooks/create). It only ever
   receives events for your own cases. See
   [Webhooks](/api-reference/webhooks/introduction).

<Tip>
  Make every write retryable. Every write endpoint accepts an `Idempotency-Key`
  header, and case vouchers additionally take a `uniqueId` that is unique per
  tenant — so a request that is replayed from a queue cannot create the same
  invoice or the same payment twice.
</Tip>

## Identifiers to store on both sides

Getting the identifier mapping right is the most important part of the
integration. Decide up front which id lives where, and persist it.

| Store in your own system | Where it comes from                                                | Used for                                                                  |
| ------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| `customerId`             | `POST /customers` response `customerId`                            | Adding case vouchers for that debtor                                      |
| `caseId`                 | `POST /customers/{customerId}/add-case-vouchers` response `caseId` | Attaching documents to the case                                           |
| `caseVoucherId`          | The same response, `caseVoucherId`                                 | Recognising a payment you pushed yourself when it comes back on a webhook |

| Store in Debbie      | Field                             | Used for                                                |
| -------------------- | --------------------------------- | ------------------------------------------------------- |
| Your customer number | `referenceId` on the customer     | Settlement — every payment is reported back under it    |
| Your invoice number  | `referenceId` on the case voucher | Registering direct payments, and posting the settlement |

<Warning>
  `customer.referenceId` is the identifier your settlement is keyed on. It must
  be the customer number from the system you post the money back into — not a
  number invented for the integration. Get it wrong and the money arrives
  without anything to match it to.
</Warning>

## 1. Create your customer

A *customer* is your relationship with the party that owes the money. It holds
one or more *users*: the people involved. Exactly one of them is the debtor.

```json POST /v1/{tenantId}/customers?creditorId={creditorId} theme={null}
{
  "isCompany": false,
  "referenceId": "CUST-40218",
  "users": [
    {
      "relation": "debtor",
      "contact": true,
      "lang": "da",
      "details": {
        "name": "Mette Jensen",
        "cpr": "0101801234",
        "email": "mette@example.com",
        "phone": { "locale": "45", "number": "12345678" },
        "address": {
          "address": "Søndergade 5, 2. th",
          "zipcode": "8000",
          "city": "Aarhus C",
          "country": "DK"
        }
      }
    }
  ]
}
```

* **One user must be the debtor.** `relation: "debtor"` is the party the claim
  is against, and the one the debt is registered on. Anyone else on the matter —
  a guardian, an administrator, a contact at a company — is
  `relation: "alternative-contact"`.
* `contact: true` marks the user Debbie addresses its letters, emails and texts
  to.
* `isCompany` says whether the customer is a business or a private individual.
  Send `cvr` on the user for a company and `cpr` for a person.
* **`referenceId` is your customer number** — the one in the system you keep
  your books in. It is what the settlement refers to, so it has to match. It
  only needs to be unique within your creditor, which is why
  `GET /customers/by-reference-id/{referenceId}?creditorId={creditorId}` finds
  the customer again instead of you keeping a mapping table.

The response gives you the `customerId`. Store it.

<Card horizontal title="API reference: Create customer">
  See [POST Create customer](/api-reference/endpoints/customers/create) for full
  details.
</Card>

## 2. Add the case vouchers for collection

A *case voucher* is one financial entry on the claim. Send the unpaid invoice,
plus any reminder fees and compensation fees you have already charged, in one
request. Debbie decides itself whether that opens a new case or extends an
existing one for the same debtor.

```json POST /v1/{tenantId}/customers/{customerId}/add-case-vouchers theme={null}
{
  "caseVouchers": [
    {
      "voucherTypeId": "eb41e58e-fccf-419d-a771-cd5027fe6e87",
      "referenceId": "D12412347",
      "uniqueId": "D12412347",
      "amount": -450000,
      "currency": "DKK",
      "date": "2026-05-02",
      "dueDate": "2026-05-16",
      "text": "Invoice 2026-05",
      "properties": { "meterNumber": "874113" }
    },
    {
      "voucherTypeId": "3e51bf87-3c8f-4dd5-abd5-69417d113c89",
      "referenceId": "D12412347-R1",
      "uniqueId": "D12412347-R1",
      "amount": -10000,
      "currency": "DKK",
      "date": "2026-06-01"
    },
    {
      "voucherTypeId": "ed3e3d61-e1bc-4ee2-9166-adf2c41a2bbf",
      "referenceId": "D12412347-K1",
      "uniqueId": "D12412347-K1",
      "amount": -31000,
      "currency": "DKK",
      "date": "2026-06-01"
    }
  ],
  "constraints": {
    "caseGroupId": "0b1a0f3c-6c1d-4b8f-9b6b-6f6a1f2e7a10"
  }
}
```

### Voucher types

`voucherTypeId` says what the entry is. These are the ones an invoice-to-cash
integration normally sends:

| What you are sending                  | `voucherTypeId`                        |
| ------------------------------------- | -------------------------------------- |
| Invoice (faktura)                     | `eb41e58e-fccf-419d-a771-cd5027fe6e87` |
| Credit note (kreditnota)              | `ef2cdc50-230f-4046-b1af-0f9c498dddd3` |
| Reminder fee (rykkergebyr)            | `3e51bf87-3c8f-4dd5-abd5-69417d113c89` |
| Compensation fee (kompensationsgebyr) | `ed3e3d61-e1bc-4ee2-9166-adf2c41a2bbf` |

The full list, including subscription and loan types, is on the
[Voucher types](/api-reference/voucher-types) page, and [Get voucher
types](/api-reference/endpoints/voucher-types/get) returns the same list at
runtime including any types your debt collection partner has defined
themselves. Fetch it once and cache it rather than hard-coding the ids.

### Amounts, references and duplicates

* **Amounts are integers in the minor unit, and debt is negative.** `-450000` is
  a debt of 4,500.00 DKK. A positive amount is a credit note or a payment.
* **`referenceId` is your invoice number.** It is what you will refer to when
  you register a direct payment, and what the settlement reports back, so send
  the number your own system knows.
* **`uniqueId` stops duplicates.** It is unique across the whole tenant, so a
  replayed request is rejected rather than creating the invoice twice. Setting
  it to the invoice number is usually right.

### You do not send `source`

Everything you send through this API is imposed by you, and is registered with
`source: "CREDITOR"` whatever the field says. Leave it out.

<Warning>
  This matters most for payments. A deposit registered with `source:
      "COLLECTOR"` reads as money your debt collection partner received on your
  behalf — the opposite of a payment made to you — and the settlement comes out
  backwards. Because the source is decided by the key you are calling with, you
  cannot get this wrong; do not try to set it.
</Warning>

### Attributes and case groups

Two mechanisms carry your own structure into the collection process:

* **Attributes (properties)** are custom fields on a case or a case voucher — a
  meter number, a contract id, a subscription plan. Your debt collection partner
  defines the property types in the platform and gives you the *handle* to use
  as the key: `"properties": { "meterNumber": "874113" }`. Attributes can be
  printed in the letters to the debtor and reported on, so anything the
  collector or the debtor needs to recognise the claim belongs here.
* **Case groups** segment your portfolio — a product line, a business unit, a
  brand. Pass `constraints.caseGroupId` and the case is opened in that group,
  which decides how the collector treats and reports on it. The id comes from
  your partner. `constraints` only applies when the vouchers open a *new* case;
  it is ignored when they land on an existing one.

The response gives you one entry per voucher, each with the `caseId` it landed
on and the `caseVoucherId` it was created as. **Store the `caseId`** — you need
it for the next step.

```json theme={null}
[
  {
    "merged": false,
    "caseId": "3f1c2d4e-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
    "caseVoucherId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
  }
]
```

<Card horizontal title="API reference: Add case vouchers">
  See [POST Add case
  vouchers](/api-reference/endpoints/customers/add-case-vouchers) for full
  details.
</Card>

## 3. Attach the documentation

A claim that is documented is a claim that survives an objection. Attach the
invoice copy, the contract, the delivery note, the photo — whatever your
partner would otherwise have to ask you for.

Documents are attached in two calls: upload the file, then create the document
that points at it.

### Upload the file

```json POST /v1/{tenantId}/files theme={null}
{
  "name": "invoice-D12412347.pdf",
  "contentType": "application/pdf",
  "content": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ..."
}
```

`content` is the file, base64 encoded. The response is the stored file:

```json theme={null}
{
  "id": "8d9f0c1e-2b3a-4c5d-6e7f-8a9b0c1d2e3f",
  "fileName": "invoice-D12412347.pdf",
  "contentType": "application/pdf"
}
```

<Note>
  The upload calls the identifier `id`; everywhere it is referenced afterwards —
  on a document, and as the `appendix` of a case voucher — the field is called
  `fileId`. It is the same value. On a document only the id is read: the name
  and content type are taken from the stored file.
</Note>

### Create the document

```json POST /v1/{tenantId}/documents theme={null}
{
  "entityId": "3f1c2d4e-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
  "entityType": "case",
  "type": "OTHER",
  "minAccessRole": "debtor",
  "file": {
    "fileId": "8d9f0c1e-2b3a-4c5d-6e7f-8a9b0c1d2e3f",
    "fileName": "invoice-D12412347.pdf",
    "contentType": "application/pdf"
  }
}
```

* **`entityId` and `entityType`** say what the document belongs to. `case` with
  the `caseId` you got back in step 2 is what you want for invoice copies and
  anything else about a specific claim. `creditor` with your own `creditorId`
  is for documents about the relationship itself — a power of attorney, a
  standing agreement — and they are not tied to a case.
* **`type` is always `OTHER`.** The other document types — foundation, verdict,
  settlement — decide whether a debt has a legal foundation and when it expires,
  and are set by your debt collection partner as the case progresses through the
  courts.
* **`minAccessRole` decides who can see it.** Consider setting it to `debtor`:
  the document then shows up in the debtor's self-service portal, so someone
  disputing an invoice can see the invoice. Leave it out and the document stays
  between you and your collection partner.

<Card horizontal title="API reference: Add document">
  See [POST Add document](/api-reference/endpoints/documents/create) for full
  details.
</Card>

## 4. Register payments made directly to you

Once a case is handed over, the debtor may still pay you rather than your debt
collection partner — into your own bank account, or through your own portal.
Those payments have to reach Debbie, or the collection process keeps chasing
money that has already arrived, and the settlement is wrong.

```json POST /v1/{tenantId}/direct-payments?creditorId={creditorId} theme={null}
{
  "caseVoucherReferenceId": "D12412347",
  "amount": 120000,
  "date": "2026-06-04",
  "currency": "DKK"
}
```

* **You reference the invoice, not the case.** `caseVoucherReferenceId` is the
  `referenceId` you sent the invoice under. Debbie finds it within your
  creditor, so it has to match exactly one case voucher. If you stored the
  `caseVoucherId` Debbie returned when the invoice was created, send that
  instead — exactly one of the two.
* **The payment goes to that one invoice.** A payment of 1,200.00 on an invoice
  of 1,200.00 settles it; a smaller amount settles part of it. A payment that
  covers several invoices has to be split by you and sent as one request per
  invoice — one payment against one entry is all this endpoint does today.
* **The amount is positive**, in the minor unit, and cannot exceed what is
  still outstanding on that invoice — earlier payments already registered
  against it count.
* **`currency` is a check, not a conversion.** If you send it, it must match the
  currency of the invoice.
* **Send an `Idempotency-Key` header**, one per request — the bank transaction
  id plus the invoice number, say, since one transfer may cover several
  invoices — so a retry within 48 hours is answered with the original result
  instead of booking the payment again. There is no `uniqueId` on a direct
  payment.
* **`source` is not a field here either.** A direct payment is by definition
  money that never passed through your collection partner, and is always
  registered as `CREDITOR`.

### When the payment is refused

Every refusal carries a `message` that says why. These are the ones to handle:

| Status | `message`                                                                        | What it means                                                                                                                    |
| ------ | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| 404    | `No case voucher found with id …` / `No case voucher found with reference id …`  | The voucher does not exist within your creditor, has been deleted, or has not been handed over as part of a case yet.            |
| 400    | `Reference id … matches more than one case voucher, …`                           | Two of your invoices share that reference id. Send `caseVoucherId` instead.                                                      |
| 400    | `Payment is in … but case voucher … is in …`                                     | The `currency` you sent does not match the invoice. Leave it out or correct it.                                                  |
| 400    | `Case voucher … is expired and can no longer be paid`                            | The claim has expired in Debbie. Contact your collection partner.                                                                |
| 400    | `Case voucher … is not a debt`                                                   | You referenced a payment or a credit note rather than an invoice or a fee.                                                       |
| 400    | `Amount … exceeds the … still outstanding on case voucher …`                     | The amount is more than what is left on the invoice, counting payments already registered against it.                            |
| 422    | `Give exactly one of caseVoucherId and caseVoucherReferenceId`, or a field error | The body is invalid: both or neither identifier was given, `amount` is not a positive integer, or `caseVoucherId` is not a UUID. |

A `400` or `404` will not succeed on retry with the same body; put those aside
for review rather than back on the queue.

<Card horizontal title="API reference: Register direct payment">
  See [POST Register direct
  payment](/api-reference/endpoints/direct-payments/create) for full details.
</Card>

## 5. Book the settlement

When your debt collection partner settles with you, a settlement is created and
`billings.create` fires with a `billingId`. That is the trigger to fetch what
the settlement is made of — the payments and how they were split, and the fees
and disbursements you are charged — and post it.

<Card horizontal title="Settlements">
  Settlement has enough detail of its own that it has a separate guide. See
  [Settlements](/guides/settlements).
</Card>

## What happens in between

Between handover and settlement, the collection process is your partner's. Three
webhooks tell you what is going on without you having to ask:

| Event                                                        | What it tells you                                                                                                                                                                              |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`cases.create`](/api-reference/webhooks/cases.create)       | A case was opened for you — including the ones your debt collection partner opens without you, from an import or by hand.                                                                      |
| [`cases.update`](/api-reference/webhooks/cases.update)       | The case changed. A status transition carries the new status, and an ended case carries the reason — paid, written off, withdrawn — which is usually what closes the claim in your own system. |
| [`cases.delete`](/api-reference/webhooks/cases.delete)       | A case was deleted.                                                                                                                                                                            |
| [`billings.create`](/api-reference/webhooks/billings.create) | A settlement was created. See [step 5](#5-book-the-settlement).                                                                                                                                |

There is no per-payment event today: money reaching a case is accounted for in
the settlement, and visible in the creditor portal meanwhile. `billings.create`
is the one your integration has to handle.

Subscribe to them under *Developers → Webhooks* in the portal or with
[Create webhook](/api-reference/endpoints/webhooks/create); a webhook of yours
only ever carries your own creditor's events.

<Warning>
  Do not rebuild the case balance in your own system. Interest accrues on rules
  agreed with your collection partner, fees follow the collection workflow, and
  expiry is recalculated as the case moves. A copy would need all of that logic
  to stay correct and would drift the moment it did not. The balance lives in
  Debbie — the creditor portal shows it, and `GET /customers/{customerId}` gives
  you the cases and their statuses. Book money in your own system, not entries:
  the payments, and the settlement.
</Warning>

## Scopes

Grant the API key only what the integration uses:

| Scope                                       | Needed for                                                     |
| ------------------------------------------- | -------------------------------------------------------------- |
| `read:customers`, `write:customers`         | Steps 1 and 2                                                  |
| `read:case-vouchers`, `write:case-vouchers` | Steps 2 and 4                                                  |
| `write:files`                               | Step 3, uploading the file                                     |
| `write:documents`                           | Step 3, attaching it                                           |
| `read:billings`                             | Step 5, reading the settlement                                 |
| `read:webhooks`, `write:webhooks`           | Managing your webhooks over the API instead of in the portal   |
| `read:voucher-types`                        | Resolving `voucherTypeId` at runtime instead of hard-coding it |

## Checklist before going live

<Steps>
  <Step title="The customer reference is the one you post money into">
    `customer.referenceId` is the customer number from the system that receives
    the settlement, and `caseVoucher.referenceId` is your invoice number.
  </Step>

  <Step title="Every write is replayable">
    `Idempotency-Key` on every request, `uniqueId` on every voucher, and failed
    calls go to a retry queue rather than being dropped.
  </Step>

  <Step title="Identifiers are stored on both sides">
    `customerId`, `caseId` and the `caseVoucherId` of every voucher you created
    are persisted in your own system.
  </Step>

  <Step title="Documentation follows the claim">
    Invoice copies and contracts are uploaded and attached to the case, and it
    is decided per document type whether the debtor should be able to see it.
  </Step>

  <Step title="Direct payments flow to Debbie the same day">
    Payments into your own account are registered against the invoice they
    settle, keyed on the bank transaction id.
  </Step>

  <Step title="The webhook receiver verifies and is idempotent">
    `X-Verification-Token` is checked, `creditorId` is checked against your own,
    the handler tolerates duplicate deliveries, and it responds 200 before doing
    downstream work.
  </Step>

  <Step title="Settlement is booked in full">
    `billings.create` triggers a fetch of both the deposit distribution and the
    billing vouchers, and the payout, the commission, the fees and the
    disbursements are all posted. See [Settlements](/guides/settlements).
  </Step>
</Steps>
