---
title: "List delivery history for a watch"
method: GET
path: "/api/v1/watches/{watch_id}/deliveries"
tags: ["Board Watches"]
---

# List delivery history for a watch

`GET /api/v1/watches/{watch_id}/deliveries`

## Request

- Base URL: `https://api.vaquill.ai`
- URL: `https://api.vaquill.ai/api/v1/watches/{watch_id}/deliveries`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/vaquill/apis/vaquill-developer-api/docs/api/v1/watches/:watch_id/deliveries/get.ts> (a function with typed inputs; its response is checked against the schema)

The most recent webhook delivery attempts for a watch you own, newest first. Only webhook/both channel watches have delivery history; an email-only watch always returns an empty list here (email sends are not logged per-attempt). Test deliveries (`POST /watches/{id}/test`) never appear, as they are not persisted.

`consecutiveFailures` counts how many of the most recent attempts failed in a row, and `deliveryHealth` is `failing` once that reaches 3. Read them before reading the list: a watch can keep firing on schedule and delivering nothing, and the streak is the only field that distinguishes that from a bad day. `consecutiveFailuresTruncated` is true when the streak fills the whole page, meaning the real run is at least this long. `deliveryHealth` is `blocked` when the newest attempt was not sent because the account's plan no longer includes webhook delivery (Business); the row's `error` says so. Page back with `beforeId` = `meta.nextBeforeId`.

Nothing notifies you when a webhook starts failing, so poll this route. We alert ourselves after 3 consecutive failures, but we send you no email and no callback, and a failing receiver is in your infrastructure rather than ours. A watch stays active and keeps being dispatched while every delivery fails, so silence from your endpoint is not evidence that the law did not change. Check `deliveryHealth` on a schedule of your own.

`error` on each attempt says why it failed, including the redirect target when an endpoint answered 3xx. Deliveries do not follow redirects: a 3xx is a failed delivery, not a delivered one, so register the endpoint's final URL.

Free.

## Path parameters

- `watch_id` string, required: Id of the watch, as returned by `POST /watches` (`id`).

## Query parameters

- `limit` integer: Max deliveries to return.
- `beforeId` integer, nullable: Return only attempts with an `id` less than this: page back past `limit` by passing `meta.nextBeforeId` (the smallest `id` on your last page). Same cursor contract as `/changes`.

## Response `200`

The watch's delivery history, newest first.

- DeliveriesEnvelope
  - `data` DeliveriesPage, required
    - `deliveries` DeliveryResponse[], required
      - `id` integer, required
      - `watchId` string, required
      - `refreshLogId` integer, nullable
      - `attemptedAt` string, required
      - `statusCode` integer, nullable
      - `success` boolean, required
      - `error` string, nullable
      - `attemptNumber` integer, required
    - `consecutiveFailures` integer, required
    - `consecutiveFailuresTruncated` boolean, required
    - `deliveryHealth` 'ok' | 'failing' | 'blocked', required
  - `meta` ResponseMeta, required: The `meta` block every board-watch route returns beside `data`.
    - `processingTimeMs` number, required
    - `creditsConsumed` number, required
    - `cursor` integer, nullable: Change feeds only: the value to send back as `sinceId`. Null on an empty page. The highest change `id` on the page, except on a full newest-first page read with a `sinceId`, where unread rows sit below the page and the cursor stays at your `sinceId`; use `nextBeforeId` to reach them.
    - `nextBeforeId` integer, nullable: Change feeds only, full `order=desc` pages: the lowest change `id` on the page. Send it as `beforeId` for the next older page. Null otherwise.
    - `hasMore` boolean, nullable: Change feeds only: true when the page filled to `limit`.

## Other responses

- `401`: Invalid or missing API key.
- `403`: API key lacks `research:read` scope.
- `404`: Watch not found.
- `422`: Validation error: `detail` plus one `errors` entry per rejected field.
- `429`: Rate limit exceeded.
- `500`: Internal error (`internal_error`, retryable). Any credits charged for the call are refunded; retry with backoff and quote `requestId` to support if it persists.

## Changes

- **2026-11-07** `340c0d24d0b6`: 1 warning, 48 info
  - added the new `blocked` enum value to the `data/deliveryHealth` response property for the response status `200`
  - added the new optional `query` request parameter `beforeId`
  - added the non-success response with the status `500`
  - added the optional property `candidateCount` to the response with the `401` status
  - …45 more

[Operation history](https://skmtc.dev/vaquill/apis/vaquill-developer-api/changes/api/v1/watches/:watch_id/deliveries/get.md)

---

[API](https://skmtc.dev/vaquill/apis/vaquill-developer-api.md) - [All operations](https://skmtc.dev/vaquill/apis/vaquill-developer-api/llms.txt) - [OpenAPI document](https://skmtc.dev/vaquill/apis/vaquill-developer-api/revisions/c3b7efdf909e?raw)
