> ## 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] Overview

> Receive real-time notifications when consumptions and payroll cycles change, without polling the API.

# Webhooks

<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 are still being finalised.
</Note>

Webhooks let Cobee notify your system the moment something changes, instead of you polling the API. You register an HTTPS endpoint and subscribe it to one or more event types; whenever a matching event occurs, Cobee sends an HTTP `POST` request to your endpoint with a signed notification.

Every webhook is a **notification, not a full data dump**: `data` identifies the relevant resources for what changed (for example, a company, employee, payroll period or committed expense) so you can call the corresponding `GET` endpoint for the authoritative state, and it may additionally include the **immutable fact** that triggered the event (e.g. the individual consumption that was just charged) — but never a computed, mutable balance, since that could go stale if a notification is delayed or delivered more than once. This keeps every event idempotent: receiving one more than once, or out of order, is always harmless.

## How It Works

1. **Register a subscription**: you provide the URL of your endpoint and the list of event types you want to receive (`POST /webhooks/subscriptions`). Cobee returns a **signing secret** that you must store securely.
2. **Receive notifications**: when an event occurs, Cobee calls your endpoint with a small signed JSON body identifying what changed.
3. **Acknowledge**: your endpoint must respond with a `2xx` status code within 10 seconds. Any other response (or a timeout) is considered a failed delivery and will be retried.
4. **Fetch the data**: call the corresponding `GET` endpoint ([consumptions](/publicApi/en/api-reference/consumptions/endpoint/get_employee_consumptions), [committed expenses](/publicApi/en/api-reference/committed-expenses/endpoint/get_committed_expenses)…) with the filters and format options that suit your integration to obtain the latest state.
5. **Monitor**: you can inspect the delivery history of your subscription at any time (`GET /webhooks/subscriptions/{subscriptionId}/deliveries`).

```mermaid theme={null}
sequenceDiagram
    participant Cobee
    participant Partner as Partner endpoint

     Note over Cobee: An employee's consumptions change
     Cobee->>Partner: POST notification { id, type, occurredAt, data }
    Partner-->>Cobee: 2xx (acknowledged)
    Partner->>Cobee: GET consumptions (fetch latest state)
    Cobee-->>Partner: current accumulated consumptions

    Note over Cobee: Delivery fails
    Cobee->>Partner: POST notification
    Partner-->>Cobee: 500 / timeout
    Note over Cobee: Retry with exponential backoff
    Cobee->>Partner: POST (same event id)
    Partner-->>Cobee: 2xx (acknowledged)
```

## Notification Envelope

Every webhook call uses the same envelope, regardless of the event type:

```json theme={null}
{
  "id": "0d9f2b1a-4c3e-4f6a-9b2d-8e7f6a5b4c3d",
  "type": "employee.consumption.generated",
  "occurredAt": "2026-07-31T12:00:00Z",
  "data": { ... }
}
```

| Field        | Description                                                                                                                                                                                                                                                                                                                                    |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | Unique identifier of the event. Use it to deduplicate deliveries.                                                                                                                                                                                                                                                                              |
| `type`       | The event type. See [Events](/en/api-reference/webhooks/events).                                                                                                                                                                                                                                                                               |
| `occurredAt` | When the event occurred, as an ISO 8601 timestamp in UTC.                                                                                                                                                                                                                                                                                      |
| `data`       | Identifies the relevant resources for what changed (for example, a company, employee, payroll period or committed expense) so you can call the right `GET` endpoint, and for some events also includes the immutable record that triggered it (see [Events](/en/api-reference/webhooks/events)). It never carries a computed, mutable balance. |

<Info>
  Webhook notifications are always `application/json`. To download the actual data you call the `GET` endpoints, which support JSON and CSV as usual.
</Info>

## Delivery Semantics

* **Notification, not a query API**: some events include the immutable record that triggered them, but you always obtain the accumulated totals, all benefit categories, `groupBy` and format by calling the `GET` endpoint when you need the authoritative current state — so the contract you consume there is the one you already know.
* **Idempotent by design**: computed, mutable balances are never embedded in the payload, so it does not matter if a notification is delivered more than once, out of order, or coalesced — replaying the notification is safe, while a subsequent `GET` returns the latest authoritative state.
* **At-least-once delivery**: the same event may be delivered more than once. Deduplicate by event `id` if you want to avoid redundant fetches.
* **No cascading calls**: when a company closes its payroll cycle you receive a single `company.payroll-cycle.closed` notification — not one call per employee. You then download the company's consumptions in one `GET` call.
* **Retries**: failed deliveries are retried with exponential backoff for up to 72 hours. After that, the delivery is marked as `failed` and can be inspected in the delivery history.

## Verifying Signatures

Every webhook request includes an `X-Cobee-Signature` header so you can verify that the call genuinely comes from Cobee and that the notification was not tampered with:

```
X-Cobee-Signature: t=1753939051,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
```

To verify:

1. Extract the timestamp (`t`) and the signature (`v1`) from the header.
2. Concatenate the timestamp, a dot (`.`), and the raw request body: `{t}.{body}`.
3. Compute the HMAC-SHA256 of that string using your subscription's **signing secret** as the key.
4. Compare your computed signature with `v1` using a constant-time comparison.
5. Reject the request if the signatures do not match, or if the timestamp is older than **5 minutes** (to prevent replay attacks).

<Warning>
  Store the signing secret securely and never expose it in client-side code or logs. If you suspect it has been compromised, rotate it with `POST /webhooks/subscriptions/{subscriptionId}/secret`.
</Warning>

## Endpoints

| Endpoint                                                  | Description                                                      |
| --------------------------------------------------------- | ---------------------------------------------------------------- |
| `POST /webhooks/subscriptions`                            | Register a new webhook subscription. Returns the signing secret. |
| `GET /webhooks/subscriptions`                             | List your webhook subscriptions.                                 |
| `GET /webhooks/subscriptions/{subscriptionId}`            | Retrieve a single subscription.                                  |
| `PATCH /webhooks/subscriptions/{subscriptionId}/enable`   | Enable a subscription.                                           |
| `PATCH /webhooks/subscriptions/{subscriptionId}/disable`  | Disable a subscription.                                          |
| `DELETE /webhooks/subscriptions/{subscriptionId}`         | Delete a subscription.                                           |
| `POST /webhooks/subscriptions/{subscriptionId}/secret`    | Rotate the signing secret.                                       |
| `GET /webhooks/subscriptions/{subscriptionId}/deliveries` | Inspect the delivery history (status, response codes, errors).   |

Authentication works exactly like the rest of the Partners API: obtain a JWT via `POST /oauth/token` and send it as a Bearer token.
