## List a batch run's recipients

**get** `/v1/invitation_batches/{batch}/recipients`

Lists a run's per-recipient results, in the order you submitted them,
one page at a time.

**Behavior**

- Page with `cursor`: pass the previous page's `next_cursor` back
  verbatim, and stop when it comes back empty. Rows are ordered by your
  own array index, which never changes, so a page cannot skip or repeat
  a row while the run progresses underneath you.
- Filter with `state` to avoid paging 10,000 rows to find 40 problems;
  `failed` or `skipped` and read one page. An unknown value is refused,
  never silently answered with an empty page.
- Unknown runs, and another brand's; return HTTP 404.

### Path Parameters

- `batch: string`

### Query Parameters

- `cursor: optional string`

  The previous page's `next_cursor`, verbatim; absent starts at your first recipient. Not a cursor this API issued returns HTTP 400 `code` 1013.

- `limit: optional number`

  Page size; absent (or non-numeric) reads as 25, above 100 reads as 100: the same page bounds every list on this API applies.

- `state: optional string`

  Only recipients in this state (`pending`, `sent`, `failed`, `cancelled`, `skipped`); absent means every row. Anything else returns HTTP 400 `code` 1066.

### Returns

- `data: array of object { idx, reference_id, state, 3 more }`

  - `idx: number`

    This row's position in the array you submitted, zero-based: the same index each refusal names (`/recipients/7/to`), and this run's identity for that row. Never renumbered: skipped rows stay in place, so the index cannot develop holes.

  - `reference_id: string`

    Your correlation handle for this recipient, echoed back.

  - `state: "pending" or "sent" or "failed" or 2 more`

    - `"pending"`

    - `"sent"`

    - `"failed"`

    - `"cancelled"`

    - `"skipped"`

  - `to: string`

    The recipient string AS YOU SENT IT, not the canonical form, so a result matches a row of your own list without you re-normalizing anything.

  - `invitation_id: optional string`

    The invitation this recipient became, once it has been attempted; read `GET /v1/invitations/{inv}` for its delivery state, the customer's response and its webhooks. Absent while the row is `pending`, and for any row that never became one (`skipped`, `cancelled`, and a `failed` row refused before an invitation existed).

  - `reason: optional object { code, message }`

    Present exactly on the rows that will not send (`skipped`, `failed`), absent on every other.

    - `code: string`

      `to_required` | `to_invalid` | `duplicate_recipient` | `consent_required` | a `reference_id` code (`too_long`, `forbidden_character`), or, for a row that failed after acceptance, the send-time refusal: `consent_required`, `chat_superseded`, `chat_closed`, or `invitation_template_not_authorized` (only here: your brand's approval was withdrawn while the run was in flight; at submit the same condition is the run-level `403 invitation_card_not_granted`).

    - `message: string`

- `next_cursor: string`

  Pass back as `?cursor=` for the next page. EMPTY when this page exhausted the run, that, not an empty `data`, is how paging ends.

### Example

```http
curl https://messages.api.linqapp.com/v1/invitation_batches/$BATCH/recipients \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
```

#### Response

```json
{
  "data": [
    {
      "idx": 1,
      "to": "tel:+15555550188",
      "reference_id": "ride-88215",
      "state": "failed",
      "reason": {
        "code": "consent_required",
        "message": "this recipient withdrew consent after the batch was accepted"
      }
    }
  ],
  "next_cursor": ""
}
```
