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

# List per-recipient deliveries

> Returns the per-recipient, per-device delivery rows for one broadcast - the answer to "did customer X receive it, on which device, and why not".

A customer holding the pass on two platforms appears twice (one row per device). Rows settle from `pending` to `delivered`/`failed` as the fan-out drains; a cleanup sweep marks anything stranded as failed, so the collection always reaches a terminal state.

Common patterns:
- **Failed-only audit:** `?status=failed` - each row carries the provider error
- **Per-customer check:** `?customer_id=cus_...`
- **Re-target failures:** collect `customer_id`s from failed rows and send a follow-up broadcast with `audience.customer_ids`

See [Pagination](/api-reference/pagination).

Scope: `notifications:read`.



## OpenAPI

````yaml /openapi.json get /v1/notifications/{id}/deliveries
openapi: 3.1.0
info:
  title: VEXiON cards Public API
  description: >-
    ## Rate limiting


    Two independent layers, checked in this order:


    1. **Per-IP throttle (pre-authentication).** Applied to every `/v1` request
    BEFORE the API key is checked, keyed on the caller's IP address:
       - **600 requests/minute** per IP - a flood ceiling, `429 rate_limit_error` / `rate_limited`.
       - **20 failed authentications per 15 minutes** per IP - a key-guessing ceiling. Only responses that are `401` (missing/invalid/revoked key) count against this ceiling; a `403` from a valid key that simply lacks a scope does not. `429 rate_limit_error` / `too_many_failed_attempts`.

       Both ceilings send `Retry-After` (seconds).
    2. **Per-key, per-bucket limiter (post-authentication).** Documented per
    endpoint (see each operation's `429` response and the `rate_limit` field on
    the API Key resource) - e.g. 60/min default, 120/min for transactions, plus
    the notification-broadcast and lookup-filter buckets described on their own
    endpoints.


    ### Rate-limit response headers


    Every rate-limited response carries the legacy `X-RateLimit-Limit` /
    `X-RateLimit-Remaining` / `X-RateLimit-Reset` family (the last is an **epoch
    timestamp**) AND the IETF standards-track headers, side by side:


    - `RateLimit-Policy: "<bucket>";q=<quota>;w=<window seconds>` - the static
    policy for the bucket that was hit.

    - `RateLimit: "<bucket>";r=<remaining>;t=<seconds until reset>` - live
    state. **`t` is a DURATION (seconds remaining), not the epoch timestamp
    `X-RateLimit-Reset` carries - the two are never the same value.**
  version: 1.0.0
servers:
  - url: https://api.vexioncards.one
    description: Production
  - url: https://migration.api.vexioncards.one
    description: Sandbox
security:
  - apiKey: []
tags:
  - name: Customers
  - name: Cards
  - name: Transactions
  - name: Notifications
  - name: Locations
  - name: Products
  - name: API keys
  - name: Organization
  - name: Webhook endpoints
  - name: Events
  - name: Custom fields
  - name: Team members
  - name: Icons
paths:
  /v1/notifications/{id}/deliveries:
    parameters:
      - name: id
        in: path
        description: Notification identifier (`ntf_` prefix).
        required: true
        example: ntf_01J9P0G7K9C0RYHXJ2K7A4N5MZ
        schema:
          type: string
          pattern: ^ntf_[A-Za-z0-9]+$
    get:
      tags:
        - Notifications
      summary: List per-recipient deliveries
      description: >-
        Returns the per-recipient, per-device delivery rows for one broadcast -
        the answer to "did customer X receive it, on which device, and why not".


        A customer holding the pass on two platforms appears twice (one row per
        device). Rows settle from `pending` to `delivered`/`failed` as the
        fan-out drains; a cleanup sweep marks anything stranded as failed, so
        the collection always reaches a terminal state.


        Common patterns:

        - **Failed-only audit:** `?status=failed` - each row carries the
        provider error

        - **Per-customer check:** `?customer_id=cus_...`

        - **Re-target failures:** collect `customer_id`s from failed rows and
        send a follow-up broadcast with `audience.customer_ids`


        See [Pagination](/api-reference/pagination).


        Scope: `notifications:read`.
      operationId: listNotificationDeliveries
      parameters:
        - name: status
          in: query
          required: false
          description: 'Filter: `pending`, `delivered`, `failed`.'
          schema:
            type: string
            enum:
              - pending
              - delivered
              - failed
          example: failed
        - name: device
          in: query
          required: false
          description: 'Filter: `apple_wallet`, `google_wallet`.'
          schema:
            type: string
            enum:
              - apple_wallet
              - google_wallet
        - name: customer_id
          in: query
          required: false
          description: One recipient's rows.
          schema:
            type: string
            pattern: ^cus_[A-Za-z0-9]+$
        - name: limit
          in: query
          required: false
          description: Page size, 1-100. Default 25.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: starting_after
          in: query
          required: false
          description: >-
            Cursor - the `ntd_` id of the last row on the previous page
            (forward). Numeric offset or page parameters are not supported;
            cursors are the only supported paging mechanism.
          schema:
            type: string
            pattern: ^ntd_[A-Za-z0-9]+$
        - name: ending_before
          in: query
          required: false
          description: Reverse cursor - rows before this `ntd_` id.
          schema:
            type: string
            pattern: ^ntd_[A-Za-z0-9]+$
      responses:
        '200':
          description: Paginated delivery rows.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationDeliveryList'
              examples:
                Mixed devices:
                  summary: Both platforms + one failure
                  value:
                    object: list
                    data:
                      - id: ntd_01J9P0G7K9C0RYHXJ2K7AD0001
                        object: notification_delivery
                        customer_id: cus_01HW9R4P2ZK7N8MTX1RVABCDEF
                        device: apple_wallet
                        status: delivered
                        error: null
                        updated_at: 1782134460
                      - id: ntd_01J9P0G7K9C0RYHXJ2K7AD0002
                        object: notification_delivery
                        customer_id: cus_01HW9R4P2ZK7N8MTX1RVMNOPQR
                        device: google_wallet
                        status: delivered
                        error: null
                        updated_at: 1782134462
                      - id: ntd_01J9P0G7K9C0RYHXJ2K7AD0003
                        object: notification_delivery
                        customer_id: cus_01HW9R4P2ZK7N8MTX1RVGHIJKL
                        device: google_wallet
                        status: failed
                        error: device unregistered
                        updated_at: 1782134470
                    has_more: false
                    url: >-
                      /v1/notifications/ntf_01J9P0G7K9C0RYHXJ2K7A4N5MZ/deliveries
                Failed only:
                  summary: '?status=failed audit'
                  value:
                    object: list
                    data:
                      - id: ntd_01J9P0G7K9C0RYHXJ2K7AD0004
                        object: notification_delivery
                        customer_id: cus_01HW9R4P2ZK7N8MTX1RVGHIJKL
                        device: google_wallet
                        status: failed
                        error: device unregistered
                        updated_at: 1782134470
                    has_more: false
                    url: >-
                      /v1/notifications/ntf_01J9P0G7K9C0RYHXJ2K7A4N5MZ/deliveries
        '400':
          description: >-
            Invalid pagination parameter: limit outside 1-100, or a malformed
            cursor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                Invalid limit:
                  summary: limit out of range
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_limit
                      message: limit must be an integer between 1 and 100.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#invalid_limit
                      request_id: req_5f8b2c4a9e1d3
                      param: limit
                Malformed cursor:
                  summary: starting_after is not a valid cursor
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_cursor
                      message: starting_after is not a valid ntd_ cursor.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#invalid_cursor
                      request_id: req_5f8b2c4a9e1d3
                      param: starting_after
          headers: {}
        '401':
          description: Missing, invalid, or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Key lacks the required scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Notification not found in your organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited - respect Retry-After.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error. Retry-safe on idempotent calls.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: api_error
                  code: internal_error
                  message: >-
                    An internal error occurred. The request may be retried with
                    the same Idempotency-Key.
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#internal_error
                  request_id: req_5f8b2c4a9e1d3
              examples:
                Http 500 default:
                  summary: Http 500 default
                  value:
                    error:
                      type: api_error
                      code: internal_error
                      message: >-
                        An internal error occurred. The request may be retried
                        with the same Idempotency-Key.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#internal_error
                      request_id: req_5f8b2c4a9e1d3
          headers: {}
components:
  schemas:
    NotificationDeliveryList:
      type: object
      description: Cursor-paginated per-recipient delivery rows.
      required:
        - object
        - data
        - has_more
      properties:
        object:
          type: string
          const: list
          description: Always `"list"`.
        data:
          type: array
          items:
            $ref: '#/components/schemas/NotificationDelivery'
          description: Delivery rows.
        has_more:
          type: boolean
          description: True when more pages exist.
        next_cursor:
          type:
            - string
            - 'null'
          description: >-
            Cursor for the next page **in the direction you are already
            paging**, or `null` when this page ends the collection. `has_more:
            true` always comes with a non-null `next_cursor`.


            Feed it back to the SAME parameter you used: `starting_after` when
            paging forward (the default), or `ending_before` when paging
            backward. Treat it as opaque and do not parse it.


            You no longer need to take the last object's `id` yourself. That
            still works and is unchanged, but deriving the cursor from the page
            put the burden of knowing the sort direction on you, and it broke
            silently on any endpoint whose cursor is not the object id.
          example: cus_iofv5j4hn6bphinmmyat27tc5y
        url:
          type: string
          description: Stable collection URL.
          example: /v1/notifications/ntf_01J9P0G7K9C0RYHXJ2K7A4N5MZ/deliveries
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - rate_limit_error
                - idempotency_error
                - api_error
            code:
              type: string
            message:
              type: string
            retry_after:
              type: integer
              description: >-
                Seconds to wait before retrying. Present on rate-limit (429) and
                idempotency-key-in-use (409) errors; mirrors the `Retry-After`
                HTTP header. Absent on error types that carry no retry guidance.
              example: 5
            param:
              type:
                - string
                - 'null'
            request_id:
              type: string
            doc_url:
              type: string
              format: uri
            field_errors:
              type: array
              description: >-
                Per-field errors on a 422. At most 100 entries: a request with
                more errors gets the first 100.
              items:
                type: object
                properties:
                  param:
                    type: string
                  code:
                    type: string
                  message:
                    type: string
                  expected_currency:
                    type: string
                    description: >-
                      On `currency_mismatch`: the business currency the request
                      must use. Absent on every other field error.
          description: >-
            The error payload: type (category), code (machine-readable), message
            (human-readable), retry_after (seconds, on
            rate-limit/idempotency-in-use errors only), param (offending field),
            request_id, doc_url, field_errors[].
      description: >-
        Standard error envelope. Branch on `error.code`; include
        `error.request_id` in support requests. See the Errors guide.
    NotificationDelivery:
      type: object
      description: >-
        One per-recipient, per-device delivery row. Answers "did customer X get
        notification Y, on which device, and if not - why". Rows exist from
        dispatch time and settle to `delivered`/`failed` as provider acks
        arrive; a post-processing sweep marks stranded rows failed.
      required:
        - id
        - object
        - customer_id
        - device
        - status
        - updated_at
      properties:
        id:
          type: string
          pattern: ^ntd_[A-Za-z0-9]+$
          description: >-
            Delivery row identifier, `ntd_` prefix - the cursor value for
            paginating this collection.
          example: ntd_01J9P0G7K9C0RYHXJ2K7AD0001
        object:
          type: string
          const: notification_delivery
          description: Always `"notification_delivery"`.
        customer_id:
          type: string
          pattern: ^cus_[A-Za-z0-9]+$
          description: The recipient.
          example: cus_01HW9R4P2ZK7N8MTX1RVABCDEF
        device:
          type: string
          enum:
            - apple_wallet
            - google_wallet
          description: >-
            The delivery channel - same vocabulary as `Customer.device`. A
            customer holding the pass on both platforms gets one row per device.
          example: apple_wallet
        status:
          type: string
          enum:
            - pending
            - delivered
            - failed
          description: >-
            `pending` → dispatch in flight. `delivered` → the device CONFIRMED
            the update (pass re-fetched / Wallet API accepted) - stronger than a
            raw push ack, still not human eyes. `failed` → terminal (stale
            token, unregistered device); see `error`.
          example: delivered
        error:
          type:
            - string
            - 'null'
          description: >-
            Failure reason on `failed` rows (e.g. 'device unregistered',
            'expired push subscription'). Null otherwise.
          example: null
        updated_at:
          type: integer
          description: Unix seconds of the last status change.
          example: 1782134460
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        Your secret API key as a Bearer token: `Authorization: Bearer
        vxn_sk_...`. Keys are created and scoped in the dashboard - see
        [Authentication](/api-reference/introduction#authentication).

````

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