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

# List the deliveries of a webhook subscription

> Lists the delivery records of a webhook subscription, newest first, with a cursor.

* **Credential:** an account API key. A trial key returns `401 unauthorized`.
* **Line-scoped keys:** the list of a [line-scoped key](/authentication#line-scoped-keys) is not
  applied here: the key reads the deliveries of the events of every line of the account.
* **`Idempotency-Key`:** not needed: the operation changes nothing. See
  [Retrying safely](/idempotency#retrying-safely).
* **Events:** none.
* **Personal data:** `request_body_snippet` contains the beginning of an event, with message text and
  handles, and `response_body_snippet` the beginning of the response of your endpoint. Request it
  only when needed, do not copy it into logs, tickets or other tools, and refer to a contact by an id
  of your own.
* **Guide:** [Delivery records](/webhooks/delivery#delivery-records) explains each field, and
  [Retention](/webhooks/delivery#retention) says how long a record exists. A record describes one
  delivery and shows its latest attempt.

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | `id` is not `wh_` followed by 25 letters or digits. `limit` is outside 1 to 100. `status` is not `pending`, `success` or `failed`. `event` is longer than 100 characters. `since` or `until` is not a timestamp in ISO 8601, in UTC, ending with `Z`. |
| 400 | `invalid_cursor` | `cursor` is not a cursor this operation returned. |
| 404 | `not_found` | Your account has no subscription with this id. |

The errors that every operation shares are in [Errors](/errors-and-rate-limits#errors).


## OpenAPI

````yaml GET /v1/webhooks/{id}/deliveries
openapi: 3.0.0
info:
  title: iSnap API
  version: 2.45.0
  description: >-
    The iSnap API sends and receives iMessage, RCS and SMS on real phone lines,
    manages those lines, and reports events through webhooks. Every message is
    sent to a real recipient, with an `isnap_live_` key and with an
    `isnap_test_` key alike. Retry a `429` after `Retry-After` seconds. Retry a
    `500` or a `503` up to 2 times, with exponential backoff. Do not retry any
    other `4xx`: fix the request. Retry a write only on an operation that honors
    `Idempotency-Key`, with the key of the first attempt. Responses carry
    personal data: handles, names and message text.
servers:
  - url: https://api.isnap.ai
security:
  - bearerAuth: []
paths:
  /v1/webhooks/{id}/deliveries:
    get:
      summary: List the deliveries of a webhook subscription
      description: >-
        Lists the deliveries of a webhook subscription, newest first. Accepts an
        API key; a line-scoped key reads the deliveries of every line. A
        delivery is one event sent to one subscription. There is one record per
        delivery, updated at each attempt: it shows the latest attempt, not a
        history of attempts. A delivery whose attempts fail is `pending` between
        two attempts, with the `response_status` of its latest attempt and a
        `next_retry_at`. When every attempt of its retry schedule fails, it
        becomes `failed` at the 7th, which starts at least 14 hours 36 minutes
        30 seconds after the first: a query with `status=failed` does not return
        the deliveries that are still being retried. Only a delivery in status
        `success` or `failed` can be replayed. A record is deleted at least 72
        hours after its creation, whatever its status. The API does not bound
        the delay. A `webhook.test` request creates no record. Filter with
        `status`, `event`, `since` and `until`. The cursor does not carry the
        filters: send the same filters on every page. Stop when `has_more` is
        `false`: `next_cursor` is then `null`. `request_body_snippet` contains
        the beginning of an event, which can include message text and handles:
        this is personal data.
      parameters:
        - schema:
            type: string
            pattern: ^wh_[0-9A-Za-z]{25}$
            description: >-
              Id of the webhook subscription, as returned in `id` by `POST
              /v1/webhooks`: `wh_` followed by 25 letters or digits.
            example: wh_3Kp8Rn9Fh2Lm5Qx7Vw1Yz4Bc6
          required: true
          description: >-
            Id of the webhook subscription, as returned in `id` by `POST
            /v1/webhooks`: `wh_` followed by 25 letters or digits.
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            description: 'Number of deliveries per page, from 1 to 100. Default: 20.'
          required: false
          description: 'Number of deliveries per page, from 1 to 100. Default: 20.'
          name: limit
          in: query
        - schema:
            type: string
            description: >-
              The `next_cursor` of the previous page. Omit it for the first
              page. Send the same filters on every page.
          required: false
          description: >-
            The `next_cursor` of the previous page. Omit it for the first page.
            Send the same filters on every page.
          name: cursor
          in: query
        - schema:
            type: string
            enum:
              - pending
              - success
              - failed
            description: >-
              Keep the deliveries in this status: `pending`, `success` or
              `failed`.
          required: false
          description: >-
            Keep the deliveries in this status: `pending`, `success` or
            `failed`.
          name: status
          in: query
        - schema:
            type: string
            maxLength: 100
            description: >-
              Keep the deliveries of this exact event type, for example
              `message.received`. Wildcards are not interpreted. At most 100
              characters.
            example: message.received
          required: false
          description: >-
            Keep the deliveries of this exact event type, for example
            `message.received`. Wildcards are not interpreted. At most 100
            characters.
          name: event
          in: query
        - schema:
            type: string
            format: date-time
            description: >-
              Keep the deliveries created at or after this timestamp: ISO 8601
              in UTC ending in `Z`. A UTC offset such as `+02:00` returns `400
              validation_failed`.
            example: '2026-10-08T12:00:00Z'
          required: false
          description: >-
            Keep the deliveries created at or after this timestamp: ISO 8601 in
            UTC ending in `Z`. A UTC offset such as `+02:00` returns `400
            validation_failed`.
          name: since
          in: query
        - schema:
            type: string
            format: date-time
            description: >-
              Keep the deliveries created at or before this timestamp: ISO 8601
              in UTC ending in `Z`. A UTC offset such as `+02:00` returns `400
              validation_failed`.
            example: '2026-10-08T18:00:00Z'
          required: false
          description: >-
            Keep the deliveries created at or before this timestamp: ISO 8601 in
            UTC ending in `Z`. A UTC offset such as `+02:00` returns `400
            validation_failed`.
          name: until
          in: query
      responses:
        '200':
          description: One page of deliveries.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveryListEnvelope'
        '400':
          description: >-
            Invalid request. `error.code` is `validation_failed` when a
            parameter, a header or the body does not match its schema:
            `error.issues` then lists each problem. It is `invalid_cursor` when
            `cursor` cannot be read.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: No valid credential. `error.code` is `unauthorized`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            Not found. `error.code` is `not_found`: the account has no webhook
            subscription with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            Too many requests. `error.code` is `rate_limit_exceeded` when more
            than 100 requests came from the same network address in 60 seconds,
            whatever their API key or account: wait `error.retry_after` seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            Unexpected failure. `error.code` is `internal_error`. An operation
            that takes a JSON body also returns it when the body, sent as
            `application/json`, is not valid JSON. Quote `trace_id` when you
            report it to iSnap.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
components:
  schemas:
    WebhookDeliveryListEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
          description: Always `true` on a success response.
        data:
          type: object
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/WebhookDeliveryV1'
              description: Deliveries, newest first.
            has_more:
              type: boolean
              description: '`true` when another page exists.'
            next_cursor:
              type: string
              nullable: true
              description: >-
                Cursor of the next page when `has_more` is `true`, otherwise
                `null`. Send it as `cursor`.
          required:
            - items
            - has_more
            - next_cursor
          description: One page of deliveries.
        trace_id:
          type: string
          description: >-
            Trace id of the request that produced this response: 32 lowercase
            hexadecimal characters. Quote it when you report a problem to iSnap.
        request_id:
          type: string
          description: >-
            Id of the request that produced this response: `req_` followed by 25
            letters or digits.
      required:
        - success
        - data
        - trace_id
        - request_id
    ErrorEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: Always `false` on an error response.
        data:
          nullable: true
          description: Always `null` on an error response.
        error:
          type: object
          properties:
            status:
              type: integer
              description: The HTTP status of the response.
            code:
              type: string
              description: >-
                Stable machine-readable code that names the refusal. Branch on
                this value.
            message:
              type: string
              description: >-
                Text for a person. It can change between versions: do not branch
                on it.
            retry_after:
              type: integer
              nullable: true
              description: >-
                Seconds to wait before the next attempt. Set on the `429`
                responses that know a delay, where the `Retry-After` header
                carries the same value. When it is above 60 seconds, do not wait
                in a request: schedule the call, or report the time. `null` on
                every other error.
            retry_at:
              type: string
              nullable: true
              description: >-
                ISO 8601 timestamp at which the refusal ends. Set only on `429
                rebind_cap_reached`. `null` on every other error.
            issues:
              type: array
              nullable: true
              items:
                type: object
                properties:
                  path:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                    description: >-
                      Where the problem is: the field names and array indexes
                      that lead to it.
                  message:
                    type: string
                    description: Text for a person.
                  code:
                    type: string
                    description: >-
                      Kind of problem. On `validation_failed`, for example
                      `invalid_type`, `invalid_value`, `invalid_format`,
                      `invalid_union`, `invalid_key`, `invalid_element`,
                      `too_small`, `too_big`, `not_multiple_of`,
                      `unrecognized_keys` or `custom`. The list is open: read
                      `path` and `message`.
                  min:
                    type: integer
                    description: >-
                      Smallest accepted value. Present only on
                      `limit_out_of_bounds`, `mac_quantity_not_a_pack`,
                      `byop_reduction_strands_mac_sessions` and
                      `byop_reduction_strands_byod_lines`.
                  max:
                    type: integer
                    description: >-
                      Largest accepted value. Present only on
                      `limit_out_of_bounds` and
                      `mac_sessions_exceed_byop_lines`, and on the `too_big`
                      issue of an `Idempotency-Key` header longer than 255
                      characters.
                required:
                  - path
                  - message
                  - code
              description: >-
                Problems found in the request, one entry each. Set on most `400
                validation_failed` responses and on the refusals that name a
                field: a refused `handle` of `POST /v1/lookup` has `issues:
                null`. `null` on every other error.
          required:
            - status
            - code
            - message
            - retry_after
            - retry_at
            - issues
          description: >-
            What went wrong. The six fields are always present; `retry_after`,
            `retry_at` and `issues` are `null` when they do not apply.
        trace_id:
          type: string
          description: >-
            Trace id of the request that produced this response: 32 lowercase
            hexadecimal characters. Quote it when you report a problem to iSnap.
        request_id:
          type: string
          description: >-
            Id of the request that produced this response: `req_` followed by 25
            letters or digits.
      required:
        - success
        - data
        - error
        - trace_id
        - request_id
      description: The body of an error response.
    WebhookDeliveryV1:
      type: object
      properties:
        id:
          type: string
          description: 'Id of the delivery: `whd_` followed by 25 letters or digits.'
        webhook_id:
          type: string
          description: Id of the webhook subscription the delivery belongs to (`wh_`).
        event_id:
          type: string
          nullable: true
          description: >-
            Id of the event delivered: `evt_` followed by 25 letters or digits.
            The same value on every attempt and on a replay. Never `null`.
        event_type:
          type: string
          nullable: true
          description: Event type, for example `message.received`. Never `null`.
        attempt_number:
          type: integer
          minimum: 0
          description: >-
            0 before the first attempt, then the number of the latest attempt,
            from 1 to 7. It restarts at 1 when iSnap resends a delivery that
            waited for a suspension to end or that stalled.
        status:
          type: string
          description: >-
            `pending`: not sent, waiting for a retry, or waiting while the
            subscription is suspended. `success`: a response from `200` to `299`
            was received. `failed`: the delivery ended without one. No other
            value.
        request_headers:
          type: object
          nullable: true
          additionalProperties:
            nullable: true
          description: >-
            Headers of the latest attempt, with the value of `webhook-signature`
            replaced by `[redacted]`. The `webhook-timestamp` header is the time
            of the latest attempt, in Unix seconds. `null` before the first
            attempt.
        request_body_snippet:
          type: string
          nullable: true
          description: >-
            First 1,024 characters of the body of the latest attempt, recorded
            even when the URL was refused and no request was sent. `null` before
            the first attempt.
        response_status:
          type: integer
          nullable: true
          description: >-
            HTTP status of the latest attempt. `null` when no HTTP response was
            received: before the first attempt, on a network error, a timeout or
            a redirect, and when no request was sent.
        response_body_snippet:
          type: string
          nullable: true
          description: >-
            First 1,024 characters of the response body of the latest attempt,
            or `null` when there is none. It is `null` for an attempt that sent
            no request because the URL no longer passed the URL checks or
            because its host did not resolve. When iSnap ends a delivery without
            an attempt, the field contains a note written by iSnap: `not sent:
            subscription inactive`, `abandoned: older than 24h`, `abandoned:
            rescued 3 times`, or a text that starts with
            `replay_enqueue_failed:`.
        delivered_at:
          type: string
          nullable: true
          description: >-
            Timestamp of the successful attempt. `null` unless `status` is
            `success`.
        next_retry_at:
          type: string
          nullable: true
          description: >-
            Timestamp of the next attempt while a retry is scheduled. `null`
            before the first attempt, once the delivery has ended, and while it
            waits for a suspended subscription to resume.
        replay_of:
          type: string
          nullable: true
          description: >-
            Id of the delivery that this one replays. `null` for an original
            delivery, and once the record of the original is deleted.
        created_at:
          type: string
          description: >-
            Timestamp of creation of the delivery. Its record is deleted at
            least 72 hours later.
      required:
        - id
        - webhook_id
        - event_id
        - event_type
        - attempt_number
        - status
        - request_headers
        - request_body_snippet
        - response_status
        - response_body_snippet
        - delivered_at
        - next_retry_at
        - replay_of
        - created_at
      description: >-
        A delivery: one event sent to one webhook subscription. There is one
        record per delivery, updated at each attempt.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        An API key created in the iSnap dashboard, or a trial key, sent as
        `Authorization: Bearer <key>`. An API key starts with `isnap_live_` or
        `isnap_test_`; both act on the same account, lines and billing. A trial
        key acts on its trial only.

````

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