Rehearse a broadcast (who would actually receive it)
Runs the send's own audience resolution and consent filter over the whole audience and reports how many contacts would actually receive the broadcast, and why the rest would not. Nothing is sent and nothing is written — no audience snapshot, no send-log row, no change to the broadcast — so a rehearsal is repeatable and costs a caller nothing but the reads.
This is the number POST /v1/broadcasts/{id}/preview does not give you.
Preview reports the targeted population and applies no consent at
all; this reports the eligible population after suppression,
account-wide unsubscribes and topic consent are applied. Both numbers are
returned here, and targeted_basis says how targeted was counted:
membership is the materialized membership fold the send itself reads,
which can differ from preview's audience.total because preview
recompiles a segment's predicate live. The two disagree while a segment
is waiting to be re-evaluated, and that disagreement is information, not
an error.
filtered breaks the drops down by the same four reasons the
per-recipient results log records after a send, so a rehearsal and a
results page are readable against each other. sample_filtered carries
up to 25 of the dropped recipients by contact id only — no email
addresses, so this endpoint is not an audience export.
The no-topic gate is reported, not raised. A marketing broadcast with
no topic, not acknowledged as topic-less, is refused by the consent
filter at send time. Rehearsing one returns no_topic_ack_required: true
with eligible: null (and the account-tier counts still filled in),
rather than a 422 — the point of a rehearsal is to show that refusal
coming.
template_ready is false when the broadcast's template is not deployed
to SES. An eligible count over an undeployed template is a reach nobody
can be sent.
Rate limited per broadcast (one rehearsal per 10 seconds by default);
429 rate_limited carries Retry-After. Returns 422 validation_failed when the audience cannot be resolved.
Path parameters
id string<uuid> required Resource UUID. An unparseable id reads as a clean 404 not_found.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
broadcast_id string<uuid> required targeted integer<int64> required The resolved audience size, before consent. See targeted_basis for how it was counted.
targeted_basis string enum required How targeted was counted. membership is the materialized membership fold the send reads, which can differ from the preview endpoint's audience.total (a live recompiled segment predicate) while a segment is waiting to be re-evaluated.
One of: membership
eligible integer<int64> | null required How many contacts would be handed to SES. Null when no_topic_ack_required is true: the send would be refused in that state, so there is no reach to report. Null is not zero.
filtered object required The dropped recipients by reason. These are the same four reasons the
per-recipient results log records on filtered rows after a send. When
eligible is non-null, these four plus eligible sum to targeted.
opted_out and not_subscribed are kept apart deliberately: the first
actively refused this topic, the second never opted in to a topic that
defaults to opt-out. Different facts, different remedies.
Fields of filtered:
suppressed integer<int64> required Hard bounce or complaint — permanently unmailable.
unsubscribed_all integer<int64> required Opted out of all email from this organization.
opted_out integer<int64> required Explicitly opted out of this broadcast's topic.
not_subscribed integer<int64> required Never opted in to this broadcast's topic, which defaults to opt-out.
no_topic_ack_required boolean required True when this broadcast has no topic and has not been acknowledged as topic-less, and is not transactional — the state in which the consent filter refuses the send outright. Reported here rather than raised.
sample_filtered array<object> required Up to 25 of the dropped recipients, in audience order. Contact ids only — no email addresses.
Constraints: 0–25 items
Each entry in sample_filtered:
contact_id string<uuid> required tier string enum required Which gate dropped this recipient.
One of: account, topic
reason string enum required One of: suppressed, unsubscribed_all, opted_out, not_subscribed
template_ready boolean required True when the broadcast's pinned template is deployed to SES with a template name recorded, or when the broadcast carries inline content and so has no template to deploy.
requires array<object> required What this broadcast still needs before it can send. An empty array means nothing is outstanding.
Each entry in requires:
kind string enum required The namespace key is looked up in. Branch on this; never parse detail.
One of: template, segment, list, topic, identity, channel, attribute, activity_property, asset
key string required The identifier as the definition names it — a template name, a segment id or key, a from-address. Empty for a requirement about the ABSENCE of a reference: a send with no topic at all names no topic.
status string enum required ok — resolves and is usable now.
missing — nothing in this organization answers to that key. Correct
the reference, or create the thing.
not_ready — it exists, in a state that cannot be used yet. Usually
a wait, and retrying the identical request is the move. Treating this
as missing sends you hunting for a typo that is not there.
One of: ok, missing, not_ready
detail string required The state, in the words that say which state — deploy_state=pending, member_count=null (evaluation queued). For display and logs; do not parse it.
fix string required The call that clears this requirement, as an HTTP route where there is one and as a plain instruction where there is not (a template deploy is waited for, not called). Empty on an ok entry.
validation_failed Problem. When the refusal is about a
reference — a segment key, template slug, topic or attribute name the
organization does not have — it additionally carries validation_code,
field, line/column, missing, candidates and next_step, so a
client can correct the call without a second round of guessing. See
ValidationProblem.
application/problem+json code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json