---
title: "List send history"
method: GET
path: "/products/notify/sends"
tags: ["notify"]
---

# List send history

`GET /products/notify/sends`

The account sends, newest first. Metadata and counters only — the audience, the pointer code, and a plaintext body are not in this projection.

## Query parameters

- `limit` integer
- `cursor` string

## Response `200`

A page of sends.

- object
  - `data` object, required
    - `sends` NotifySendSummary[], required
      - `send_id` string, nullable, required
      - `created_at` integer, nullable — Unix seconds.
      - `title` string, nullable
      - `category` string, nullable
      - `delivery` 'sealed' | 'plaintext' | 'null', nullable
      - `display_sender_name` string, nullable — The "from" the recipient saw. Display only; nothing routes on it.
      - `recipient_count` integer, nullable
      - `enqueued_count` integer, nullable
      - `delivered_count` integer, nullable
      - `failed_count` integer, nullable
      - `state` 'unknown' | 'pending' | 'sending' | 'delivered' | 'partial' | 'failed', required — DERIVED, never stored — and derived by the SAME function the send-history list and the status rollup both call, so the two surfaces can never disagree about what a state means. unknown = no audience denominator (a row written before the counters existed); pending = accepted, nothing handed to delivery yet; sending = in flight; delivered = every recipient reached a channel that accepted; partial = every recipient resolved, some delivered and some not; failed = every recipient resolved and none delivered. The list feeds it the send row's best-effort counters; the status view recomputes them from the authoritative per-recipient rows, so the list can lag but cannot contradict. Use the per-recipient `state` for why a given recipient did not get it.
    - `cursor` string, nullable, required — Opaque continuation token. Null when the listing is exhausted.
  - `meta` ResponseMeta — Envelope metadata present on every successful response.
    - `api_version` string
    - `request_id` string
    - `response_ms` integer
    - `timestamp` integer — Unix seconds.
    - `pools` object — Storage and bandwidth capacity snapshot for the account.
    - `billing` object
      - `past_due` boolean
    - `quota` object — This minute of the account request budget. Present on authenticated success responses that passed the throttle; absent otherwise.
      - `limit` integer
      - `used` integer
      - `remaining` integer
      - `resets_at` integer — Unix seconds.

## Other responses

- `400` — VALIDATION_ERROR — the message names the offending field.
- `401` — AUTHENTICATION_ERROR — missing or invalid credentials.
- `403` — FORBIDDEN — the caller may not act on this account.
- `429` — RATE_LIMIT_EXCEEDED — the account exceeded 300 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
- `503` — SEND_HISTORY_PROVISIONING — the history index is not queryable on this deployment. Temporary; retry shortly. Sending and delivery are unaffected.

---

[API](https://skmtc.dev/sparkvault/apis/sparkvault-api.md) · [All operations](https://skmtc.dev/sparkvault/apis/sparkvault-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/sparkvault/sparkvault-api/revisions/aaf3fd4981d9/schema)
