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

# Pull webhooks

> Poll pending webhook items, process them, and acknowledge them

Pull webhooks let your integration fetch events on a schedule without hosting a
webhook receiver. Debbie stores each matching event as a `PENDING` webhook item.
Your job fetches the items, processes them, and marks each completed item as
`SUCCESS` so it is no longer returned by the pending filter.

## Before you start

Use an [API key](https://documentation.debbiecollect.com/api-reference/authentication.md) with `read:webhooks` to fetch
items and `write:webhooks` to create the subscription and acknowledge items.
Replace `{tenantId}` and `{token}` in the examples with your tenant id and API
key. The webhook subscription's id is `{webhookId}`; each event has a separate
`{itemId}`.

## 1. Create a pull webhook

In the platform, open **Developers → Webhooks** and add a webhook:

1. Enter a name and select the events your integration needs.
2. Open **Advanced settings**. Under **Other settings** ("Andre indstillinger"
   in Danish), turn **Pull webhooks** on.
3. Save the webhook. You can leave the delivery URL empty.

Alternatively, use [Create webhook](https://documentation.debbiecollect.com/api-reference/endpoints/webhooks/create.md)
with `pull: true`:

```bash theme={null}
curl --request POST "https://api.debbiecollect.com/v1/{tenantId}/webhooks" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "ERP case sync",
    "events": ["cases.create"],
    "pull": true
  }'
```

Save the returned `id` as `{webhookId}`. If you created the webhook in the
platform, find its `id` using [Get webhooks](https://documentation.debbiecollect.com/api-reference/endpoints/webhooks/get-list.md).
A pull webhook does not POST events to a URL. It collects new matching events
after creation; creating one does not export existing cases.

## 2. Fetch pending items

Set up a scheduled job that calls
[Get webhook items](https://documentation.debbiecollect.com/api-reference/endpoints/webhooks/get-items.md) with
`state=PENDING`. Filter by `webhookId` to fetch only this subscription's items:

```bash theme={null}
curl --get "https://api.debbiecollect.com/v1/{tenantId}/webhooks/items" \
  -H "Authorization: Bearer {token}" \
  --data-urlencode "state=PENDING" \
  --data-urlencode "webhookId={webhookId}" \
  --data-urlencode "page=0" \
  --data-urlencode "pageSize=20"
```

The response contains `items` and pagination `meta`. For example, with other
item fields omitted:

```json theme={null}
{
  "items": [
    {
      "id": "867ddc9d-ee15-4493-b7fe-f6fbd6bbe71b",
      "hookId": "b963f219-255e-47b7-a3f2-0fe6180514a1",
      "event": "cases.create",
      "state": "PENDING",
      "data": {
        "caseId": "3f1c2d4e-5a6b-4c8d-9e0f-1a2b3c4d5e6f",
        "id": 1042,
        "referenceId": "INV-2026-0042",
        "customerId": "9b8a7c6d-5e4f-4a2b-9c0d-9e8f7a6b5c4d",
        "creditorId": "0f6c7a3e-6e4e-4f0c-9a2a-2f6cba3f9a11"
      }
    }
  ],
  "meta": { "currentPage": 0, "pageSize": 20 }
}
```

Use `item.event` to choose how to process `item.data`. In this example,
`item.id` is the webhook item to acknowledge; `item.data.id` is the case number.
See [cases.create](https://documentation.debbiecollect.com/api-reference/webhooks/cases.create.md) for this event's payload.
Fetching an item does not change its state or reserve it for your worker.

## 3. Acknowledge each processed item

After successfully processing and saving an item's result in your system, call
[Patch webhook item](https://documentation.debbiecollect.com/api-reference/endpoints/webhooks/patch-item.md) with that
item's `id` and `{"state":"SUCCESS"}`:

```bash theme={null}
curl --request PATCH \
  "https://api.debbiecollect.com/v1/{tenantId}/webhooks/items/{itemId}" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{"state":"SUCCESS"}'
```

The endpoint returns `200` with `{}`. The item remains stored but no longer
appears in requests filtered by `state=PENDING`.

## 4. Fetch the next batch

After acknowledging a batch, repeat the GET request with **`page=0`**. Continue
until `items` is empty, then wait until the next scheduled poll.

> [!WARNING]
>
> Do not increment the page number while acknowledging items. Successful items
> leave the pending results, so the next batch moves to page 0. Requesting page
> 1 would skip pending items.

## Retries and duplicate processing

* If processing fails, leave the item `PENDING`. Record the failure and retry
  later. If a batch has failures, wait before fetching it again and alert on
  repeated failures rather than repeatedly polling the same items without a delay.
* If processing succeeds but the PATCH fails or times out, the item may appear
  again. Persist the processed `item.id` with your result so a retry can
  acknowledge it without repeating the work.
* Run one polling job per subscription at a time, or coordinate workers in your
  own system. Two workers can fetch the same pending item.
* Items are returned newest first. Do not assume that events for a case arrive
  in the order in which they occurred.

For an initial check, create a case after subscribing to `cases.create`, fetch
its pending item, and acknowledge it. Repeat the same pending request and
confirm that the item's `id` is no longer returned.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.