> ## Documentation Index
> Fetch the complete documentation index at: https://docs.partners.api.cobee.io/llms.txt
> Use this file to discover all available pages before exploring further.

# [DRAFT] Events

> The event types your webhook subscriptions can receive, and their notifications.

# Webhook Events

<Note>
  **Coming soon**: The Webhooks API is currently under design review. This documentation describes the target interface and may change before release. The exact event identifiers shown below are provisional and pending final confirmation.
</Note>

All events share the same [envelope](/en/api-reference/webhooks/overview#notification-envelope). Every event is **idempotent by design**: it does not matter if a notification is delivered more than once, out of order, or coalesced.

<Info>
  Two kinds of data can appear in `data`:

  * **Identifiers** (company, payroll cycle, employee) — always present, so you can call the matching `GET` endpoint for the current, authoritative state.
  * **The fact that triggered the event** (e.g. the consumption that was just charged) — included directly when it is an **immutable record**: replaying or re-delivering it is harmless, since it always describes the same past fact. A **computed, mutable balance** (like a running total or "amount still pending") is never embedded, since a delayed or duplicated delivery could overwrite a more recent state with a stale one — for that you always call the `GET` endpoint.
</Info>

## Event Types at a Glance

### Consumption & Payroll Cycle

| Event                                                               | Fires when                                                                              |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| [`employee.consumption.generated`](#employee-consumption-generated) | A real new consumption (charge or void) changes an employee's accumulated consumptions. |
| [`company.payroll-cycle.closed`](#company-payroll-cycle-closed)     | A company's payroll cycle is closed and its reconciliation is complete.                 |

### Committed Expenses

| Event                                                                                           | Fires when                                                           |
| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| [`employee.committed-expense.requested`](#employee-committed-expense-requested)                 | A new committed expense is created / approved.                       |
| [`employee.committed-expense.finished`](#employee-committed-expense-finished)                   | The last scheduled payment of a committed expense has been executed. |
| [`employee.committed-expense.cancelled`](#employee-committed-expense-cancelled)                 | A committed expense (or its remaining future payments) is cancelled. |
| [`employee.committed-expense.payment.cancelled`](#employee-committed-expense-payment-cancelled) | A single scheduled payment is cancelled or voided.                   |

***

## Consumption & Payroll Cycle Events

### `employee.consumption.generated`

Sent when an employee's accumulated consumptions change because of a **real new consumption** (a purchase is authorized and charged, a void reverts a consumption within the same cycle, etc.). `consumption` describes that individual transaction — the fact that triggered the event.

Fetch the accumulated totals from [`GET /companies/{companyId}/employees/{employeeId}/consumptions`](/publicApi/en/api-reference/consumptions/endpoint/get_employee_consumptions) whenever you want the current state **across all benefit categories** (for reconciliation, a full refresh, or simply to double-check); for most integrations, the `consumption` data in the event is already enough to keep your own running totals up to date.

<Info>
  The end-of-cycle **reconciliation** is not a new consumption and does **not** trigger this event: reconciliation only consolidates what was already reported. You are only notified when a genuine new consumption occurs.
</Info>

<Info>
  The `period` field identifies the payroll period in `YYYY-MM` format.
</Info>

<Expandable title="Example">
  ```json theme={null}
  {
    "id": "0d9f2b1a-4c3e-4f6a-9b2d-8e7f6a5b4c3d",
    "type": "employee.consumption.generated",
    "occurredAt": "2026-07-31T12:00:00Z",
    "data": {
      "company": {
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "currency": "EUR",
        "period": "2026-07",
        "employee": {
          "id": "dd3de3a0-f903-42a0-b729-4cb16f185bc6",
          "consumption": {
            "category": "nursery-benefit",
            "behaviour": "flex",
            "sumType": "expenses",
            "amountInCents": 10000
          }
        }
      }
    }
  }
  ```
</Expandable>

<Info>
  `consumption.sumType` is `expenses` for a charge and `withdrawn` for a void/reversal — same enum as the `GET` endpoint.
</Info>

### `company.payroll-cycle.closed`

Sent once a company's payroll cycle is closed and its **reconciliation is complete** — not while reconciliation is in progress. Use this event to know when a company's consumptions for the cycle are final.

Fetch them by calling [`GET /companies/{companyId}/consumptions`](/publicApi/en/api-reference/consumptions/endpoint/get_company_consumptions) with the closed cycle's `payroll` and your preferred `groupBy`, `categories` and format options.

<Info>
  If a late event affecting the cycle arrives after it was closed (for example, two weeks later), the cycle is reconciled again and a new `company.payroll-cycle.closed` event is emitted. Always fetch the latest state when you receive it.
</Info>

<Info>
  There is intentionally **no corporation-level event**: the companies of a corporation do not necessarily close their payroll cycles on the same day. If you operate at corporation level, listen to `company.payroll-cycle.closed` for each company and act when all of them have closed.
</Info>

<Expandable title="Example">
  ```json theme={null}
  {
    "id": "3f8a2c1d-9b7e-4d5a-8c6f-2e1d0b9a8f7e",
    "type": "company.payroll-cycle.closed",
    "occurredAt": "2026-07-31T18:00:00Z",
    "data": {
      "company": {
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "period": "2026-07"
      }
    }
  }
  ```
</Expandable>

## Committed Expense Events

<Note>
  **Coming soon**: these events are still under design review and may change before release. They **supersede** the single `employee.committed-expense.updated` event previously sketched here: instead of one generic notification, the lifecycle of a committed expense is now split into more specific events so you always know *which* change happened — without needing to diff two snapshots yourself.
</Note>

A **committed expense** is an amount committed under an employee's account over several payroll cycles (for example, nursery), grouped by `fiscalYear` and benefit `category`, and split into monthly **committed expense payments**. The employee in the event identifies the account holder. If the expense applies to a dependent instead, `recipient` identifies that dependent. Two levels of events exist:

* **Committed expense** (`employee.committed-expense.*`): the aggregate for a `fiscalYear` + `category` changed — it was requested, finished, or cancelled. `committedAmountInCents` and `paidAmountInCents` are included as a snapshot at the time of the event, but the **remaining pending amount is a mutable, computed balance** — fetch it from the `GET` endpoint below rather than deriving it yourself.
* **Committed expense payment** (`employee.committed-expense.payment.cancelled`): a single monthly instalment of that committed expense was cancelled. A single instalment is an **immutable record** — like a regular consumption — so its `payment.amountInCents` is included directly in the event.

Fetch the current aggregate — `committedAmountInCents`, `paidAmountInCents`, `numberOfPayments` and `pendingPayments` — from [`GET /committed-expenses?employeeId={employeeId}&fiscalYear={fiscalYear}`](/en/api-reference/committed-expenses/endpoint/get_committed_expenses) (coming soon) whenever you receive an `employee.committed-expense.*` event and you need the authoritative, current remaining limit, rather than computing it yourself. For `employee.committed-expense.payment.cancelled`, the `payment.amountInCents` value in the notification is already enough to know the amount released by that specific cancelled instalment.

### `employee.committed-expense.requested`

Sent when a new committed expense is **created / approved**.

<Expandable title="Example">
  ```json theme={null}
  {
    "id": "5b4c3d2e-1f0a-4b9c-8d7e-6f5a4b3c2d1e",
    "type": "employee.committed-expense.requested",
    "occurredAt": "2026-02-05T12:00:00Z",
    "data": {
      "company": {
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "currency": "EUR",
        "employee": {
          "id": "dd3de3a0-f903-42a0-b729-4cb16f185bc6",
          "committedExpense": {
            "id": "f235bb3a-204a-47cf-ad22-2e48a590cdce",
            "category": "nursery-benefit",
            "fiscalYear": "2026",
            "recipient": {
              "id": "8a1b2c3d-4e5f-6789-a0b1-c2d3e4f56789"
            },
            "committedAmountInCents": 1000000,
            "paidAmountInCents": 0
          }
        }
      }
    }
  }
  ```
</Expandable>

<Info>
  `paidAmountInCents` is always present on every `employee.committed-expense.*` event, so you don't need to special-case any of them. On `requested` it is always `0`: nothing has been charged yet.
</Info>

<Info>
  `recipient` is available only in the aggregate committed expense events (`requested`, `finished` and `cancelled`). It identifies the person the committed expense applies to when that person is different from the employee. It is omitted when the committed expense applies directly to the employee, so the employee's identifier is not duplicated. The `employee.committed-expense.payment.cancelled` event does not include `recipient.id`.
</Info>

### `employee.committed-expense.finished`

Sent when the **last** scheduled payment of a committed expense has been executed and nothing is left pending. This is a terminal state: no further events are expected for this `fiscalYear` + `category` unless a new committed expense is requested.

<Expandable title="Example">
  ```json theme={null}
  {
    "id": "5b4c3d2e-1f0a-4b9c-8d7e-6f5a4b3c2d1e",
    "type": "employee.committed-expense.finished",
    "occurredAt": "2026-12-31T12:00:00Z",
    "data": {
      "company": {
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "currency": "EUR",
        "employee": {
          "id": "dd3de3a0-f903-42a0-b729-4cb16f185bc6",
          "committedExpense": {
            "id": "f235bb3a-204a-47cf-ad22-2e48a590cdce",
            "category": "nursery-benefit",
            "fiscalYear": "2026",
            "recipient": {
              "id": "8a1b2c3d-4e5f-6789-a0b1-c2d3e4f56789"
            },
            "committedAmountInCents": 1000000,
            "paidAmountInCents": 500000
          }
        }
      }
    }
  }
  ```
</Expandable>

### `employee.committed-expense.cancelled`

Sent when a committed expense is **cancelled / rejected as a whole**, or when its **remaining future payments** are cancelled (a partial cancellation) — this can happen before or after some payments have already been executed.

<Info>
  An amount cannot be modified in place — a modification is a cancellation of the existing committed expense (this event) plus the creation of a new one (`employee.committed-expense.requested`), so you receive both notifications.
</Info>

<Expandable title="Example">
  ```json theme={null}
  {
    "id": "5b4c3d2e-1f0a-4b9c-8d7e-6f5a4b3c2d1e",
    "type": "employee.committed-expense.cancelled",
    "occurredAt": "2026-12-31T12:00:00Z",
    "data": {
      "company": {
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "currency": "EUR",
        "employee": {
          "id": "dd3de3a0-f903-42a0-b729-4cb16f185bc6",
          "committedExpense": {
            "id": "f235bb3a-204a-47cf-ad22-2e48a590cdce",
            "category": "nursery-benefit",
            "fiscalYear": "2026",
            "recipient": {
              "id": "8a1b2c3d-4e5f-6789-a0b1-c2d3e4f56789"
            },
            "committedAmountInCents": 1000000,
            "paidAmountInCents": 200000
          }
        }
      }
    }
  }
  ```
</Expandable>

### `employee.committed-expense.payment.cancelled`

Sent when a **single** scheduled payment is cancelled or voided without cancelling the rest of the committed expense (for example, a manual incident voids one month, or a payment is withdrawn after delivery but the remaining months are kept). `payment.amountInCents` contains the amount released back to the employee's limit; the commitment itself continues for the remaining payments.

<Expandable title="Example">
  ```json theme={null}
  {
    "type": "employee.committed-expense.payment.cancelled",
    "occurredAt": "2026-10-18T13:42:07Z",
    "data": {
      "company":{
        "id": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
        "currency": "EUR",
        "employee":{
          "id": "dd3de3a0-f903-42a0-b729-4cb16f185bc6",
          "committedExpense": {
            "id": "f235bb3a-204a-47cf-ad22-2e48a590cdce",
            "payment": {
              "amountInCents": 100000
            }
          }
        }
      }
    }
  }
  ```
</Expandable>

## Manage Your Subscriptions

Once you know which events you need, register a subscription and pick which of these types to receive:

| Endpoint                                                                                                                           | Description                                                      |
| ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| [`POST /webhooks/subscriptions`](/en/api-reference/webhooks/endpoint/post_webhook_subscription)                                    | Register a new webhook subscription for one or more event types. |
| [`GET /webhooks/subscriptions`](/en/api-reference/webhooks/endpoint/get_webhook_subscriptions)                                     | List your webhook subscriptions.                                 |
| [`GET /webhooks/subscriptions/{subscriptionId}`](/en/api-reference/webhooks/endpoint/get_webhook_subscription)                     | Retrieve a single subscription.                                  |
| [`PATCH /webhooks/subscriptions/{subscriptionId}/enable`](/en/api-reference/webhooks/endpoint/patch_enable_webhook_subscription)   | Enable a subscription.                                           |
| [`PATCH /webhooks/subscriptions/{subscriptionId}/disable`](/en/api-reference/webhooks/endpoint/patch_disable_webhook_subscription) | Disable a subscription.                                          |
| [`DELETE /webhooks/subscriptions/{subscriptionId}`](/en/api-reference/webhooks/endpoint/delete_webhook_subscription)               | Delete a subscription.                                           |
| [`POST /webhooks/subscriptions/{subscriptionId}/secret`](/en/api-reference/webhooks/endpoint/post_rotate_webhook_secret)           | Rotate the signing secret.                                       |
| [`GET /webhooks/subscriptions/{subscriptionId}/deliveries`](/en/api-reference/webhooks/endpoint/get_webhook_deliveries)            | Inspect the delivery history (status, response codes, errors).   |

See the [Webhooks Overview](/en/api-reference/webhooks/overview) for how to set up a subscription end-to-end, verify signatures, and understand delivery semantics.
