Invitation Batches
Invitations in bulk, as an asynchronous batch with its own status and per-row outcome.
Submit a batch of invitations
Get a batch run's progress
List a batch run's recipients
Cancel a batch run
ModelsExpand Collapse
InvitationBatchCreateResponse object { accepted_count, recipient_count, validate_only, skipped } validate_only’s report: what a live submit of the same body would have accepted, with no run created.
validate_only’s report: what a live submit of the same body would have accepted, with no run created.
InvitationBatchRetrieveResponse object { id, accepted_count, counts, 8 more } A run’s progress. Poll this, not the recipients.
A run’s progress. Poll this, not the recipients.
How many recipients the run will attempt: recipient_count minus the rows the submit screen skipped. The same number the submit response returned, and frozen with it.
counts: object { cancelled, failed, pending, 2 more } How many of a run’s recipients stand in each state. EVERY key is always present, zero included: a client reads five numbers instead of branching on which keys exist, and a state that has not happened yet is 0 rather than missing.
How many of a run’s recipients stand in each state. EVERY key is always present, zero included: a client reads five numbers instead of branching on which keys exist, and a state that has not happened yet is 0 rather than missing.
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.
How many recipients the run was SUBMITTED with, frozen at creation; dropped rows included. Always the length of the array you sent, and always counts’ total.
status: "queued" or "running" or "completed" or "cancelled"queued (created, nothing attempted yet) → running (at least one
recipient attempted, some still pending) → completed (no recipient is
pending; every one is sent, failed, cancelled or skipped), or
cancelled when you stopped it. completed does NOT mean every invitation
was delivered: it means the run has nothing left to attempt, and per-row
outcomes are in counts and the recipient results.
queued (created, nothing attempted yet) → running (at least one
recipient attempted, some still pending) → completed (no recipient is
pending; every one is sent, failed, cancelled or skipped), or
cancelled when you stopped it. completed does NOT mean every invitation
was delivered: it means the run has nothing left to attempt, and per-row
outcomes are in counts and the recipient results.
Last change to the run row itself (its status). NOT bumped by a per-recipient transition, so it is a run-lifecycle timestamp and not a progress heartbeat; read counts for progress.
eta: optional object { at, queued_ahead, rate_per_second, 2 more } The completion estimate. Absent when none can honestly be quoted; see the model.
The completion estimate. Absent when none can honestly be quoted; see the model.
The same estimate as a timestamp on OUR clock; seconds from when this response was composed, so a client does not have to correct for its own skew.
How many other invitations of yours are queued ahead of this run’s remaining rows in the same brand queue. A run does not clear in isolation; another campaign submitted first is counted here, and so are this run’s own recipients that have already been handed to the delivery queue.
InvitationBatchListRecipientsResponse object { data, next_cursor } One page of a run’s recipients, in your submitted order.
One page of a run’s recipients, in your submitted order.
data: array of object { idx, reference_id, state, 3 more }
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.
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.
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.
Present exactly on the rows that will not send (skipped, failed), absent on every other.
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).
InvitationBatchCancelResponse object { id, accepted_count, cancellation, 6 more } A stopped run: where it now stands, and exactly what the cancel caught.
A stopped run: where it now stands, and exactly what the cancel caught.
How many recipients the run would have attempted: recipient_count minus the rows the submit screen skipped.
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.
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.
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.
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.
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.
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.
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.
How many recipients the run was SUBMITTED with, frozen at creation. Always counts’ total.
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.
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.