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

# Collection Stage Events

> Fired whenever a claim moves to a new collection stage.

Sent every time a claim moves to a new collection stage — including the very first stage assignment on claim creation (`from_stage` is `null` in that case).

The event `type` you'll see in the body and in the `X-IcoLaw-Event` header is **`claim.collection_stage_changed`**.

| Property        | Value                                                         |
| --------------- | ------------------------------------------------------------- |
| **Event type**  | `claim.collection_stage_changed`                              |
| **HTTP method** | `POST`                                                        |
| **Target URL**  | Your configured webhook URL                                   |
| **Headers**     | See [Webhooks Overview → Headers](/webhooks/overview#headers) |
| **API version** | `2026-05-14`                                                  |

***

## Payload

```json theme={null}
{
  "id": "evt_01HXYZ8K9MGV9C3K3F2K8AB12C",
  "type": "claim.collection_stage_changed",
  "created_at": "2026-05-14T11:30:00.000Z",
  "api_version": "2026-05-14",
  "data": {
    "claim_id": "5f3c1b22-3f4b-4d2e-9b1a-7c8e9d0f1a2b",
    "partner_reference": "INV-2024-00891",
    "from_stage": "first_demand_letter",
    "to_stage": "second_demand_letter",
    "changed_at": "2026-05-14T11:29:58.000Z"
  }
}
```

### Envelope fields

<ResponseField name="id" type="string" required>
  Stable event identifier (`evt_…`). The same `id` is reused for every retry of the same event — use it to deduplicate. It is **not** the value of the `X-IcoLaw-Delivery` header, which is a separate UUID, also stable across retries; dedupe on one of the two, consistently.
</ResponseField>

<ResponseField name="type" type="string" required>
  Always `claim.collection_stage_changed` for this event.
</ResponseField>

<ResponseField name="created_at" type="string (RFC 3339)" required>
  Server-side timestamp at which the event was emitted.
</ResponseField>

<ResponseField name="api_version" type="string" required>
  Payload contract version (`YYYY-MM-DD`). Currently `2026-05-14`. Pin this in your handler to be notified of non-additive shape changes before they reach production.
</ResponseField>

### `data` fields

<ResponseField name="data.claim_id" type="uuid" required>
  ID of the claim whose stage just changed. Use it to look up the full claim via [`GET /openapi/claims/{id}`](/api-reference/claims/get-claim).
</ResponseField>

<ResponseField name="data.partner_reference" type="string | null">
  The `partner_reference` you provided when creating the claim through the API, surfaced verbatim so you can correlate back to your own system without an extra API call. For a claim created by incaseof.law, its `external_reference`.
</ResponseField>

<ResponseField name="data.from_stage" type="enum | null" required>
  Previous collection stage. `null` on the very first stage assignment (claim creation).

  Possible values (14): `new`, `in_processing`, `first_demand_letter`, `second_demand_letter`, `third_demand_letter`, `installment_payment`, `installment_cancelled`, `dunning_action_recommended`, `exekutionsantrag_recommended`, `warten_auf_zinstermin`, `manuelle_pruefung`, `mzr_aufhebungserklaerung_sent`, `mzr_klage_recommended`, `closed` — see the [reference](#stage-value-reference) below.
</ResponseField>

<ResponseField name="data.to_stage" type="enum" required>
  New collection stage that just took effect. Same value set as `from_stage` (excluding `null`).
</ResponseField>

<ResponseField name="data.changed_at" type="string (RFC 3339)" required>
  When the stage transition was persisted server-side. Use this for reconciliation — see [Reconcile out-of-order events](/webhooks/overview#reconcile-out-of-order-events).
</ResponseField>

***

## Stage value reference

| Value                           | Meaning                                                                                                                                                                                                                                                                                                                                        |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `new`                           | Claim just created, no action taken yet.                                                                                                                                                                                                                                                                                                       |
| `in_processing`                 | Initial intake / verification stage.                                                                                                                                                                                                                                                                                                           |
| `first_demand_letter`           | First demand letter (CL1) sent.                                                                                                                                                                                                                                                                                                                |
| `second_demand_letter`          | Second demand letter (CL2) sent.                                                                                                                                                                                                                                                                                                               |
| `third_demand_letter`           | Third demand letter (CL3) sent.                                                                                                                                                                                                                                                                                                                |
| `installment_payment`           | Debtor entered an installment agreement.                                                                                                                                                                                                                                                                                                       |
| `installment_cancelled`         | Instalment agreement cancelled after a missed instalment (Terminsverlust). The debtor may still settle the missed instalment; no judicial step has been recommended yet.                                                                                                                                                                       |
| `dunning_action_recommended`    | Out-of-court collection exhausted — judicial dunning recommended.                                                                                                                                                                                                                                                                              |
| `dunning_action_filed`          | The judicial dunning action has been filed electronically: the Mahnklage for an Austrian claim, the application for a Mahnbescheid for a German one. Set automatically when the filing is dispatched — it records the transmission, not the court's acceptance; a claim filed before this stage existed stays at `dunning_action_recommended`. |
| `exekutionsantrag_recommended`  | Title obtained — enforcement (Exekutionsantrag) recommended.                                                                                                                                                                                                                                                                                   |
| `warten_auf_zinstermin`         | Tenancy claims only: waiting for the next rent due date.                                                                                                                                                                                                                                                                                       |
| `manuelle_pruefung`             | Tenancy claims only: manual legal review (e.g. after a payment during the tenancy procedure).                                                                                                                                                                                                                                                  |
| `mzr_aufhebungserklaerung_sent` | Tenancy claims only: termination declaration (Aufhebungserklärung) sent.                                                                                                                                                                                                                                                                       |
| `mzr_klage_recommended`         | Tenancy claims only: action for rent and eviction (Mietzins- und Räumungsklage) recommended.                                                                                                                                                                                                                                                   |
| `closed`                        | Claim closed (paid, written off, withdrawn or otherwise resolved) — the reason comes with [`claim.status_changed`](/webhooks/claim-status-changed).                                                                                                                                                                                            |

This is the complete list — the database accepts no other value. The four tenancy stages occur only on rental claims.

***

## Example: initial claim creation

When a claim is first created the event has `from_stage: null`. This signal is useful for "ingest into our CRM" workflows.

```json theme={null}
{
  "id": "evt_01HXYZ8K9MGV9C3K3F2K8AB12D",
  "type": "claim.collection_stage_changed",
  "created_at": "2026-05-14T09:00:01.000Z",
  "api_version": "2026-05-14",
  "data": {
    "claim_id": "5f3c1b22-3f4b-4d2e-9b1a-7c8e9d0f1a2b",
    "partner_reference": "INV-2024-00891",
    "from_stage": null,
    "to_stage": "new",
    "changed_at": "2026-05-14T09:00:00.500Z"
  }
}
```

## Example: first demand letter sent

```json theme={null}
{
  "id": "evt_01HXYZ8K9MGV9C3K3F2K8AB12E",
  "type": "claim.collection_stage_changed",
  "created_at": "2026-05-18T08:30:00.000Z",
  "api_version": "2026-05-14",
  "data": {
    "claim_id": "5f3c1b22-3f4b-4d2e-9b1a-7c8e9d0f1a2b",
    "partner_reference": "INV-2024-00891",
    "from_stage": "in_processing",
    "to_stage": "first_demand_letter",
    "changed_at": "2026-05-18T08:29:55.000Z"
  }
}
```

## Example: claim closed

```json theme={null}
{
  "id": "evt_01HXYZ8K9MGV9C3K3F2K8AB12F",
  "type": "claim.collection_stage_changed",
  "created_at": "2026-06-02T14:10:00.000Z",
  "api_version": "2026-05-14",
  "data": {
    "claim_id": "5f3c1b22-3f4b-4d2e-9b1a-7c8e9d0f1a2b",
    "partner_reference": "INV-2024-00891",
    "from_stage": "first_demand_letter",
    "to_stage": "closed",
    "changed_at": "2026-06-02T14:09:58.000Z"
  }
}
```

***

## Handler example (Node.js + Express)

End-to-end: verify signature, deduplicate, dispatch.

```js theme={null}
const express = require('express')
const crypto = require('crypto')

const app = express()
const SECRET = process.env.WEBHOOK_SECRET
const seen = new Map()  // replace with Redis / DB in production

// IMPORTANT: use raw body parser so the signature verifies against bytes
app.post(
  '/webhooks/incaseof',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const sig = req.headers['x-icolaw-signature'] || ''
    if (!verify(req.body, sig, SECRET)) {
      return res.status(401).end()
    }

    const deliveryId = req.headers['x-icolaw-delivery']
    if (seen.has(deliveryId)) {
      return res.status(200).end()  // already processed
    }
    seen.set(deliveryId, Date.now())

    const event = JSON.parse(req.body.toString('utf8'))

    if (event.type === 'claim.collection_stage_changed') {
      await onStageChanged(event.data)
    } else if (event.type === 'claim.status_changed') {
      await onStatusChanged(event.data)
    }
    // Any other type: acknowledge and ignore — new types may be added.

    res.status(200).end()
  }
)

async function onStageChanged(data) {
  // Reconcile against your local mirror by changed_at
  const local = await db.claims.get(data.claim_id)
  if (local?.last_stage_changed_at >= data.changed_at) return  // stale event

  await db.claims.upsert({
    id: data.claim_id,
    stage: data.to_stage,
    last_stage_changed_at: data.changed_at,
  })

  // If you need details (debtor, amounts, etc) fetch via the API:
  // const claim = await icoApi.get(`/openapi/claims/${data.claim_id}`)
}

function verify(rawBody, header, secret) {
  // ... see Webhooks Overview → Reference implementations
}

app.listen(3000)
```

<Tip>
  Use the **"Send test event"** button in the admin UI to fire a synthetic event with `claim_id: "00000000-0000-0000-0000-000000000000"`. Filter that ID out in your real handler — it's intentionally a non-existent claim so you can sanity-check end-to-end without polluting your data.
</Tip>
