Skip to content
Linq

Cancel a batch run

POST/v1/invitation_batches/{batch}/cancel

Cancels a run: every recipient not yet attempted is stopped before any attempt.

Behavior

  • Stops both the recipients still waiting to become invitations and the ones already sitting in the delivery queue; cancelling seconds into a large run does not let the hundreds already queued go out.
  • Cannot stop a request already on Apple’s wire or anything Apple has accepted. The cancellation object reports those as in_flight and already_sent rather than counting them as cancelled.
  • Exact, not best-effort: a cancel and the run’s own fan-out serialize on the run, so there is no window where a recipient is both cancelled and sent.
  • Safe to repeat, never refused. Cancelling a cancelled run returns HTTP 200 with the same body. Cancelling a completed run also works; completed means every recipient became an invitation, not that all were delivered, and a large run is routinely completed with thousands of sends still queued; this call still stops everything unattempted. If nothing was left, cancellation is all zeros. Read the numbers, not the status code.
  • Stopped recipients appear as cancelled in counts and on the recipients page (?state=cancelled). No per-recipient webhook fires for them; the run’s one invitation_batch.completed event fires with status: "cancelled" and the final counts.
  • Unknown runs, and another brand’s; return HTTP 404.
Path ParametersExpand Collapse
batch: string
ReturnsExpand Collapse
id: string

Run id (invb_…).

accepted_count: number

How many recipients the run would have attempted: recipient_count minus the rows the submit screen skipped.

formatint32
cancellation: object { already_sent, in_flight, queued, unreleased }

What a cancel actually stopped, split by how far each recipient had got.

The four numbers exist because two of them are NOT cancelled and you have to know which. Cancel is EXACT for everything that has not been attempted and cannot recall anything that has: a request already on Apple’s wire is on Apple’s wire.

unreleased + queued is what this call stopped. in_flight + already_sent is what went out anyway. The four PARTITION the run; every recipient that became an invitation is in exactly one of them, and so is every recipient we stopped before it became one, so nothing falls between them. All four are recomputed from the run’s rows on every call, so cancelling twice reports the same split rather than a set of zeros.

already_sent: number

The send was already ATTEMPTED before your cancel landed. NOT stopped, and not recallable; by us or by anyone. Whether Apple accepted it is that invitation’s own delivery state; this counts the attempt, so a send that was attempted and refused is here rather than nowhere.

formatint32
in_flight: number

An attempt was on the wire when your cancel landed. NOT stopped: it either reached Apple or did not, and we report rather than guess.

formatint32
queued: number

Released to the delivery queue but never attempted. STOPPED; this is the set that makes a cancel three seconds into a 50,000-recipient run mean something.

formatint32
unreleased: number

Never handed to the delivery queue at all; recipients we had not reached yet, plus invitations the pacer had not released. STOPPED.

formatint32
counts: object { cancelled, failed, pending, 2 more }

The run’s recipients by state, the same object the progress read returns. Rows this cancel stopped before they were attempted are now cancelled; a row that had already become an invitation stays sent: the invitation exists, and whether it went out is that invitation’s own delivery state.

cancelled: number

Stopped by a cancel before it was attempted.

formatint32
failed: number

Accepted at submit, then permanently refused before sending; consent withdrawn in between, a superseded or closed chat, a revoked template grant. The row’s reason says which.

formatint32
pending: number

Not yet attempted. This is the number the ETA projects.

formatint32
sent: number

Handed to the invitation pipeline: an invitation_id exists on that row. Delivery to Apple is reported by that invitation’s own status and webhooks, not here.

formatint32
skipped: number

Dropped by the SUBMIT screen under on_invalid: skip_invalid, with the reason the submit response gave. Frozen at creation; nothing later moves a row into or out of it.

formatint32
created_at: string
formatdate-time
recipient_count: number

How many recipients the run was SUBMITTED with, frozen at creation. Always counts’ total.

formatint32
status: "cancelled" or "completed"

cancelled when this call (or an earlier one) stopped a live run. completed when the run’s own fan-out had ALREADY finished; every recipient was materialized before you cancelled; in which case the run keeps that status and cancellation reports what the cancel still caught in the delivery queue. It is never any other value: those two are the only states a run can be in after this call.

One of the following:
"cancelled"
"completed"
template_id: string
updated_at: string

When the run reached cancelled. Unchanged by a repeated cancel.

formatdate-time

Cancel a batch run

curl https://messages.api.linqapp.com/v1/invitation_batches/$BATCH/cancel \
    -X POST \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
{
  "id": "invb_0d5c19",
  "status": "cancelled",
  "template_id": "invt_a4f2c718d0",
  "recipient_count": 2,
  "accepted_count": 2,
  "counts": {
    "pending": 0,
    "sent": 1,
    "failed": 0,
    "cancelled": 1,
    "skipped": 0
  },
  "cancellation": {
    "unreleased": 1,
    "queued": 0,
    "in_flight": 0,
    "already_sent": 1
  },
  "created_at": "2026-08-06T14:00:00Z",
  "updated_at": "2026-08-06T14:00:31Z"
}
Returns Examples
{
  "id": "invb_0d5c19",
  "status": "cancelled",
  "template_id": "invt_a4f2c718d0",
  "recipient_count": 2,
  "accepted_count": 2,
  "counts": {
    "pending": 0,
    "sent": 1,
    "failed": 0,
    "cancelled": 1,
    "skipped": 0
  },
  "cancellation": {
    "unreleased": 1,
    "queued": 0,
    "in_flight": 0,
    "already_sent": 1
  },
  "created_at": "2026-08-06T14:00:00Z",
  "updated_at": "2026-08-06T14:00:31Z"
}