# Batches

Source: https://parasend.com/docs/api/batches

First-class container of deliveries sharing files and policies, with an aggregated monotonic funnel. Lifecycle draft → send out (/finalize) → sending → completed.

## Endpoints

- [Create a batch](https://parasend.com/docs/api/batches/create.md): `POST /v1/batches`
- [List batches](https://parasend.com/docs/api/batches/list.md): `GET /v1/batches`
- [Retrieve a batch](https://parasend.com/docs/api/batches/retrieve.md): `GET /v1/batches/{id}`
- [Update a batch](https://parasend.com/docs/api/batches/update.md): `PATCH /v1/batches/{id}`
- [Add deliveries to a batch](https://parasend.com/docs/api/batches/add-deliveries.md): `POST /v1/batches/{id}/deliveries`
- [List a batch's deliveries](https://parasend.com/docs/api/batches/deliveries.md): `GET /v1/batches/{id}/deliveries`
- [Check a batch before sending](https://parasend.com/docs/api/batches/preflight.md): `GET /v1/batches/{id}/preflight`
- [Send out a batch](https://parasend.com/docs/api/batches/send-out.md): `POST /v1/batches/{id}/finalize`
- [Get a batch report](https://parasend.com/docs/api/batches/report.md): `GET /v1/batches/{id}/report`
- [Pause a batch's follow-ups](https://parasend.com/docs/api/batches/pause.md): `POST /v1/batches/{id}/pause`
- [Resume a batch's follow-ups](https://parasend.com/docs/api/batches/resume.md): `POST /v1/batches/{id}/resume`
- [Change a batch's follow-up policy](https://parasend.com/docs/api/batches/change-policy.md): `POST /v1/batches/{id}/policy_change`
- [Extend a batch's deliveries](https://parasend.com/docs/api/batches/extend-expiry.md): `POST /v1/batches/{id}/extend_expiry`
- [Duplicate a batch](https://parasend.com/docs/api/batches/duplicate.md): `POST /v1/batches/{id}/duplicate`
- [Cancel a batch](https://parasend.com/docs/api/batches/cancel.md): `POST /v1/batches/{id}/cancel`

## The Batch object

- `id` (string, optional): The batch's ID, starting `bat_`.
- `object` (string, optional): Always `batch`.
- `name` (string · nullable, optional): The batch's name, for your own reference. A batch created without one is named `Batch` and the creation date (UTC), e.g. `Batch 2026-11-06`; null if it was set to null.
- `status` (string, optional): `draft`, then `scheduled` (optional), `sending`, `following_up` and `completed`; or `cancelled`. Paused follow-ups are a flag, not a status. One of: `draft`, `scheduled`, `sending`, `following_up`, `completed`, `cancelled`.
- `sending_mode` (string, optional): `on_finalize`: deliveries added to the draft wait and go out together when you send the batch out (`POST /batches/{id}/finalize`), or at `scheduled_at`. `rolling`: the batch is sending from the start and each delivery goes out as soon as it is added, until you close the batch with `POST /batches/{id}/finalize`. One of: `on_finalize`, `rolling`.
- `funnel` (object, optional): Counts that only go up: `notified` ≥ `opened` ≥ `authenticated` ≥ `confirmed_direct`, and `delivered_total` = `confirmed_direct` + `rescued_by_followups`.
  - `funnel.total` (integer, optional): Deliveries counted.
  - `funnel.notified` (integer, optional): Deliveries whose first notice has gone out.
  - `funnel.opened` (integer, optional): Notified deliveries whose recipient opened the link, or got further.
  - `funnel.authenticated` (integer, optional): Notified deliveries whose recipient identified themselves, or got further (every delivered one included).
  - `funnel.confirmed_direct` (integer, optional): Deliveries whose recipient confirmed receipt after the first notice, with no follow-up.
  - `funnel.rescued_by_followups` (integer, optional): Deliveries whose recipient confirmed receipt only after a follow-up.
  - `funnel.delivered_total` (integer, optional): All delivered deliveries: `confirmed_direct` plus `rescued_by_followups`.
  - `funnel.expired` (integer, optional): Deliveries that expired before the recipient confirmed receipt.
  - `funnel.revoked` (integer, optional): Deliveries that were revoked.
  - `funnel.auth_failed` (integer, optional): Deliveries whose status is now `auth_failed`: the recipient's last attempt to identify themselves failed.
  - `funnel.bounced` (integer, optional): Deliveries that ended `bounced`: the recipient could not be reached.
  - `funnel.paper_pending` (integer, optional): Deliveries in a paper-mail step (`paper_pending_approval` or `paper_dispatched`).
  - `funnel.needs_address` (integer, optional): Deliveries waiting for a postal address. Currently always 0.
  - `funnel.bounce_rate` (number, optional): `bounced` as a fraction of `notified`, from 0 to 1, rounded to four decimals; 0 when none have been notified.
  - `funnel.median_time_to_confirm` (string · nullable, optional): ISO 8601 duration.
- `created` (string · date-time, optional): When it was created, in ISO 8601 (UTC).
- `follow_up_policy_id` (string · nullable, optional): The follow-up policy (`rpol_…`) the batch's deliveries follow, unless a delivery follows its own. null: no follow-up policy (never your organisation's default policy).
- `allow_policy_override` (boolean, optional): Whether a delivery in the batch may follow a different follow-up policy, named in its `options.reminder_policy`. When false, such a delivery is refused with `policy_override_not_allowed`.
- `follow_ups_paused` (boolean, optional): Whether the batch's follow-ups are paused (`POST /batches/{id}/pause`). Reminders that come due while paused wait, and go in the next send window after `POST /batches/{id}/resume`.
- `scheduled_at` (string · date-time · nullable, optional): When the batch is to be sent out, in ISO 8601 (UTC), if you gave it a send time; null otherwise. Sending out a batch with a future `scheduled_at` makes it `scheduled` until that time.
- `send_window` (object, optional)
  - `send_window.start` (string, required): Local time HH:MM, inclusive.
  - `send_window.end` (string, required): Local time HH:MM, exclusive; after start, same day.
  - `send_window.tz` (string, optional): The time zone of `start` and `end`: always `Atlantic/Reykjavik`, which is UTC all year.
- `defaults` (object, optional)
  - `defaults.options` (object, optional)
    - `defaults.options.notify_channels` (array of string, optional): How the recipient is told — the invitation and every reminder go to each channel listed; each needs its contact (recipient.email, recipient.phone). Default — e-mail when there is an address, else SMS. Told on both, a failure on one channel bounces the delivery only once the other cannot arrive either. One of: `email`, `sms`.
    - `defaults.options.auth_methods` (array of string, optional): Which ways of identifying the recipient to offer within `recipient.verification_level`, in your order of preference. The recipient may always use a way at least as strong as the weakest one you list. Left out: every way the level allows (an SMS code only when the recipient has a phone). One of: `audkenni`, `smart_id`, `sms_otp`, `passkey`, `videoid`, `password`, `link_only`.
    - `defaults.options.reminder_policy` (string · nullable, optional): The follow-up policy's ID (`rpol_…`): the reminders this delivery sends while it waits.
    - `defaults.options.brand_profile_id` (string · nullable, optional): The brand profile's ID (`bp_…`). One set on the delivery wins over the batch's, and the batch's over the account's default.
    - `defaults.options.expires_in_days` (integer, optional): How many days the recipient has to confirm receipt before the delivery expires: a whole number from 1 to 365. Default 30. For a delivery in a batch, the days count from when the batch is sent out.
    - `defaults.options.sender_display` (string, optional): What the recipient and the evidence show as the sender. `legal_person` (your organisation's default) shows the organisation, with an opaque reference to the person who sent it; that person's name, personal code and e-mail are not shown. `individual` names the person who sent it as well. Your choice per delivery or template; a personal account is always `individual`. One of: `legal_person`, `individual`.
    - `defaults.options.allow_parasend_links` (boolean, optional): Send even though your brand profile's link domain is not working, with Parasend's own links (warning `link_domain_unavailable`). Without it such a delivery is refused with 409 `link_domain_unavailable`. Default: `false`.
    - `defaults.options.notice_wording` (object, optional): Which saved wording the first notice uses, per channel: a wording ID, null for Parasend's own, or left out for your organisation's default.
      - `defaults.options.notice_wording.email` (string · nullable, optional)
      - `defaults.options.notice_wording.sms` (string · nullable, optional)
  - `defaults.additional_security_source` (string · nullable, optional): `csv_personal_code`: a delivery added with a `recipient.personal_code`, and no `recipient.conditional_authentication` of its own, gets `conditional_authentication: true`, so only that person can open it. null or left out: no such rule. One of: `csv_personal_code`.
  - `defaults.language` (string, optional): The `language` (`is` or `en`) for deliveries added to the batch that do not set their own.
  - `defaults.message` (string, optional): The `message` for deliveries added to the batch that do not set their own: the plain-text note the recipient sees before signing in.
  - `defaults.title` (string, optional): The `title` for deliveries added to the batch that do not set their own: the subject the recipient sees in the notice and on their page.
  - `defaults.verification_level` (string, optional): The `recipient.verification_level` (`high`, `substantial`, `medium` or `low`) for deliveries added to the batch that do not set their own.
- `files` (array of string, optional): IDs of the files (`fil_…`) shared by every delivery in the batch. Each delivery added gets these first, then its own files.
- `accepting_deliveries` (boolean, optional): Whether you can add deliveries to the batch now: true for a `draft`, and for a `rolling` batch that is sending and has not been closed.
- `counts` (object, optional): The batch's deliveries by where they stand now. Each delivery is in exactly one of `delivered`, `in_progress` and `ended`, so the three add up to `total`.
  - `counts.total` (integer, optional)
  - `counts.delivered` (integer, optional)
  - `counts.in_progress` (integer, optional)
  - `counts.ended` (integer, optional): Expired, revoked, bounced or failed — not delivered.
- `created_by` (object, optional): Who created the batch: `user_id`, the member or the API key's holder (null for a key with no holder), and `via`, `portal` or `api_key`.
  - `created_by.user_id` (string · nullable, optional)
  - `created_by.via` (string · nullable, optional): One of: `portal`, `api_key`.
- `livemode` (boolean, optional): `true` in live mode, `false` in test mode.
- `sent_out_at` (string · date-time · nullable, optional): When the batch was sent out, in ISO 8601 (UTC); for a `rolling` batch, when it was closed to new deliveries. null until then; a batch cancelled first gets the time it was cancelled.
- `following_up_at` (string · date-time · nullable, optional): When the batch moved to `following_up`, in ISO 8601 (UTC): it had been sent out and every delivery in it had had its first notice. null until then.
- `completed_at` (string · date-time · nullable, optional): When the batch became `completed`, in ISO 8601 (UTC): every delivery in it had reached an end state. null until then.
- `cancelled_at` (string · date-time · nullable, optional): When the batch was cancelled with `POST /batches/{id}/cancel`, in ISO 8601 (UTC); null if it has not been.
- `follow_up_funnel` (object, optional): Only on GET /batches/{id}.
  - `follow_up_funnel.as_of` (string · date-time, optional): When the counts were taken, in ISO 8601 (UTC).
  - `follow_up_funnel.policy_id` (string · nullable, optional): The follow-up policy the counts are limited to, from the `policy_id` query parameter (`rpol_none` for deliveries with no policy); null when they cover the whole batch.
  - `follow_up_funnel.total` (integer, optional): Deliveries counted.
  - `follow_up_funnel.not_yet_notified` (integer, optional): Deliveries whose first notice has not gone out yet, such as those in a batch not yet sent out.
  - `follow_up_funnel.notice` (object, optional): The first notice: `sent` is every notified delivery and `bounced` those whose notice bounced; the outcome counts are the deliveries that had no reminder. `email_opened` counts notices whose e-mail was opened before any reminder went (a hint, not proof).
    - `follow_up_funnel.notice.sent` (integer, optional): Deliveries that reached this stage: for the notice, every notified delivery; for a reminder, the deliveries that got at least this many reminders.
    - `follow_up_funnel.notice.bounced` (integer, optional): Deliveries whose message at this stage bounced.
    - `follow_up_funnel.notice.delivered` (integer, optional): Of the deliveries for which this was the last stage, those delivered.
    - `follow_up_funnel.notice.in_progress` (integer, optional): Of the deliveries for which this was the last stage, those still under way.
    - `follow_up_funnel.notice.unreachable` (integer, optional): Of the deliveries for which this was the last stage, those that could not be reached (bounced).
    - `follow_up_funnel.notice.expired` (integer, optional): Of the deliveries for which this was the last stage, those that expired.
    - `follow_up_funnel.notice.revoked` (integer, optional): Of the deliveries for which this was the last stage, those revoked.
    - `follow_up_funnel.notice.not_matched` (integer, optional): Of the deliveries for which this was the last stage, those that ended `consignment_failed`: the recipient's identification did not meet what the delivery required.
    - `follow_up_funnel.notice.renotified_via_lookup` (integer, optional)
    - `follow_up_funnel.notice.email_opened` (integer, optional): Notices whose e-mail was opened (a hint, not proof).
    - `follow_up_funnel.notice.renotified_delivered` (integer, optional): Deliveries confirmed after a notice to a channel found by the lookup.
  - `follow_up_funnel.reminders` (array of object, optional): One entry per reminder, in order (`number` 1, 2, 3…): `sent` is the deliveries that got at least that many reminders, `bounced` those for which it bounced, and the outcome counts the deliveries whose last reminder it was. `channels` counts that reminder by channel.
    - `follow_up_funnel.reminders[].sent` (integer, optional): Deliveries that reached this stage: for the notice, every notified delivery; for a reminder, the deliveries that got at least this many reminders.
    - `follow_up_funnel.reminders[].bounced` (integer, optional): Deliveries whose message at this stage bounced.
    - `follow_up_funnel.reminders[].delivered` (integer, optional): Of the deliveries for which this was the last stage, those delivered.
    - `follow_up_funnel.reminders[].in_progress` (integer, optional): Of the deliveries for which this was the last stage, those still under way.
    - `follow_up_funnel.reminders[].unreachable` (integer, optional): Of the deliveries for which this was the last stage, those that could not be reached (bounced).
    - `follow_up_funnel.reminders[].expired` (integer, optional): Of the deliveries for which this was the last stage, those that expired.
    - `follow_up_funnel.reminders[].revoked` (integer, optional): Of the deliveries for which this was the last stage, those revoked.
    - `follow_up_funnel.reminders[].not_matched` (integer, optional): Of the deliveries for which this was the last stage, those that ended `consignment_failed`: the recipient's identification did not meet what the delivery required.
    - `follow_up_funnel.reminders[].number` (integer, optional)
    - `follow_up_funnel.reminders[].channels` (object, optional)
      - `follow_up_funnel.reminders[].channels.email` (integer, optional)
      - `follow_up_funnel.reminders[].channels.sms` (integer, optional)
      - `follow_up_funnel.reminders[].channels.alternate_contact` (integer, optional)
  - `follow_up_funnel.outcome` (object, optional): Where every counted delivery stands now, each in exactly one: `delivered`, `in_progress`, `unreachable` (bounced), `expired`, `revoked`, or `not_matched` (ended `consignment_failed`: the recipient's identification did not meet what the delivery required).
    - `follow_up_funnel.outcome.delivered` (integer, optional)
    - `follow_up_funnel.outcome.in_progress` (integer, optional)
    - `follow_up_funnel.outcome.unreachable` (integer, optional)
    - `follow_up_funnel.outcome.expired` (integer, optional)
    - `follow_up_funnel.outcome.revoked` (integer, optional)
    - `follow_up_funnel.outcome.not_matched` (integer, optional)
  - `follow_up_funnel.confirmed_direct` (integer, optional): Deliveries whose recipient confirmed receipt after the first notice, with no follow-up.
  - `follow_up_funnel.rescued_by_followups` (integer, optional): Deliveries whose recipient confirmed receipt only after a follow-up.
  - `follow_up_funnel.delivered_total` (integer, optional): All delivered deliveries: `confirmed_direct` plus `rescued_by_followups`.
- `kpis` (object, optional): Only on GET /batches/{id}.
  - `kpis.delivered` (integer, optional): Deliveries in the batch that were delivered, on the first notice or after follow-ups.
  - `kpis.delivered_pct` (number, optional): `delivered` as a percentage of the batch's deliveries, rounded to one decimal (e.g. `87.5`); 0 for an empty batch.
  - `kpis.in_progress` (integer, optional): Deliveries still under way: not yet delivered and not ended.
  - `kpis.median_time_to_delivery` (string · nullable, optional): ISO 8601 duration.
  - `kpis.bounce_rate` (number, optional): The share of notified deliveries that could not be reached (bounced), as a fraction from 0 to 1 rounded to four decimals; 0 when none have been notified.
  - `kpis.rescued_by_followups` (integer, optional): Delivered deliveries whose recipient confirmed only after a follow-up, not on the first notice.
  - `kpis.spend` (object, optional): An estimate of the batch's spend so far: the SMS messages sent for it (`sms_messages`), priced at today's prices, excluding VAT (`amount`, in the minor unit of `currency`). `lookups` is 0, and `estimated` is always true: your invoice is what counts.
    - `kpis.spend.sms_messages` (integer, optional)
    - `kpis.spend.lookups` (integer, optional)
    - `kpis.spend.amount` (integer, optional)
    - `kpis.spend.currency` (string, optional)
    - `kpis.spend.estimated` (boolean, optional)
  - `kpis.vs_previous` (object · nullable, optional): How this batch compares with the previous batch your organisation sent out in the same mode (cancelled batches left out): that batch's ID and the difference in delivered percentage, in percentage points. null until this batch is sent out, or when there is no earlier batch.
    - `kpis.vs_previous.batch_id` (string, optional)
    - `kpis.vs_previous.delivered_pct_delta` (number, optional): Percentage points.

```json
{
  "id": "bat_01M6R27T8SXWNM911FS53W4S9F",
  "object": "batch",
  "name": "November notices",
  "status": "draft",
  "sending_mode": "on_finalize",
  "follow_up_policy_id": null,
  "allow_policy_override": true,
  "follow_ups_paused": false,
  "scheduled_at": null,
  "send_window": {
    "tz": "Atlantic/Reykjavik",
    "end": "21:00",
    "start": "09:00"
  },
  "defaults": {},
  "files": [],
  "accepting_deliveries": true,
  "counts": {
    "total": 0,
    "delivered": 0,
    "in_progress": 0,
    "ended": 0
  },
  "created_by": {
    "user_id": "usr_01M6R27SYD4B50414S0BZ4KBKR",
    "via": "api_key"
  },
  "livemode": false,
  "created": "2026-11-06T09:15:05.881Z",
  "sent_out_at": null,
  "following_up_at": null,
  "completed_at": null,
  "cancelled_at": null,
  "follow_up_funnel": {
    "as_of": "2026-11-06T09:15:05.894Z",
    "policy_id": null,
    "total": 0,
    "not_yet_notified": 0,
    "notice": {
      "delivered": 0,
      "in_progress": 0,
      "unreachable": 0,
      "expired": 0,
      "revoked": 0,
      "not_matched": 0,
      "sent": 0,
      "bounced": 0,
      "renotified_via_lookup": 0,
      "renotified_delivered": 0,
      "email_opened": 0
    },
    "reminders": [],
    "outcome": {
      "delivered": 0,
      "in_progress": 0,
      "unreachable": 0,
      "expired": 0,
      "revoked": 0,
      "not_matched": 0
    },
    "confirmed_direct": 0,
    "rescued_by_followups": 0,
    "delivered_total": 0
  },
  "funnel": {
    "total": 0,
    "notified": 0,
    "opened": 0,
    "authenticated": 0,
    "confirmed_direct": 0,
    "rescued_by_followups": 0,
    "delivered_total": 0,
    "expired": 0,
    "revoked": 0,
    "auth_failed": 0,
    "bounced": 0,
    "paper_pending": 0,
    "needs_address": 0,
    "bounce_rate": 0,
    "median_time_to_confirm": null
  },
  "kpis": {
    "delivered": 0,
    "delivered_pct": 0,
    "in_progress": 0,
    "median_time_to_delivery": null,
    "bounce_rate": 0,
    "rescued_by_followups": 0,
    "spend": {
      "sms_messages": 0,
      "lookups": 0,
      "amount": 0,
      "currency": "ISK",
      "estimated": true
    },
    "vs_previous": null
  }
}
```
