Rehearse a broadcast (who would actually receive it)

Search Documentation

Search across all developer documentation

broadcasts

Rehearse a broadcast (who would actually receive it)

POST /v1/broadcasts/{id}/rehearse
Auth required api.broadcasts.view

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.

200 The rehearsal result application/json
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.

401 Missing, malformed, or unknown API key application/problem+json
403 Key lacks the required scope or plan limit violated application/problem+json
404 Resource not found application/problem+json
422 A query parameter, path value or body field failed validation. The body is a 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
429 Per-org rate limit exceeded application/problem+json
500 Unexpected server-side failure. The code is internal_error. The request_id field can be quoted to SendOps support to investigate. application/problem+json