# Batches API

Canonical: https://tyxter.com/docs/api-reference/batches
Section: API reference

Create and inspect broadcast batches and failure exports.

# Batches API

Use POST /v1/batches to create a broadcast batch. Use GET /v1/batches, GET /v1/batches/{id}, pause/resume/cancel actions, and failure export endpoints to operate the batch lifecycle.

An inline recipient's `to` follows the same rule as a single send's `recipient.id`: 8–15 ASCII digits with an optional leading `+` and only ASCII spaces (U+0020), parentheses, dots, or hyphens as separators. A malformed `to` returns 400 `invalid_phone_e164` with `param: recipients.N.to` for the first offending recipient, and the message lists up to five malformed indexes. No batch is accepted.

Before a batch is accepted, Tyxter resolves the approved TemplateVersion. Every inline recipient can satisfy modeled BODY requirements through variables in that binding's format: exact numeric keys for POSITIONAL or exact parameter-name keys for NAMED. A required text-HEADER or dynamic URL-button parameter has no batch request source, so it returns 400 template_param_mismatch with param template. The audience and audience_id shapes have no per-recipient variable source in either format and likewise cannot supply BODY, text-HEADER, or dynamic URL-button parameters, although template.header_media remains available for a modeled shared media header. A mismatch returns 400 template_param_mismatch with a corrective param such as recipients.0.variables, template.header_media, or template. No batch was accepted, billed, queued, or sent to Meta. A later approval change or unmodeled Meta behavior can still fail an accepted recipient asynchronously with the same code.

The create response carries a pacing forecast when Tyxter can make one: pacing.deferred_recipient_count is how many recipients are expected to wait for WhatsApp's daily NEW-recipient allowance on the sending number, and pacing.estimated_windows is roughly how many daily allowance windows the batch will span, counting the current one. A batch spanning more than one window is normal and is not an error — the waiting recipients stay queued and send by themselves as allowance frees up, and each one reports status_reason messaging_limit_pacing while it waits. Both numbers are ESTIMATES taken at accept: they move as other traffic on the same number consumes allowance, as recipients turn out to already be inside an open conversation window (those cost nothing), and as Meta raises or lowers the number's tier. There is deliberately no completion timestamp: the allowance window is a rolling one whose slots expire individually, so there is no single moment it resets and any timestamp would be read as a schedule.

pacing is null on GET /v1/batches/{id} and on GET /v1/batches. The forecast is an accept-time snapshot that Tyxter neither stores nor recomputes, so capture it from the create response. Null also means "no estimate" on the create response itself — the sending number could not be resolved, its allowance is not a finite number to divide by, or it has no finite cap at all — and it never means "no pacing". For live progress read enqueued_count and sent_count.

## Recipient guard skips

Mandatory Tyxter guards apply to marketing in sandbox and production. Identical template name, language, parameters and media content remain blocked until an inbound message after the previous attempt; there is no timed duplicate expiry within retained message history. Reuploading identical bytes under a new asset ID does not change content. A read receipt is not engagement. Different content on a healthy sender is allowed. A seven-day sender/recipient cooldown applies only when phone or template quality is YELLOW/RED or a policy warning occurred within seven days. An inbound message after the latest marketing attempt exempts duplication and that cooldown, even after the 24-hour service window closes. General opt-outs and Meta marketing stop preferences take priority; only a Meta marketing resume clears that provider preference. Meta 131049 requires at least 24 hours before retrying even after engagement; two consecutive 131026 refusals or a latest 130472 holdout require new inbound engagement. Utility and authentication are outside these marketing guards; general consent still applies. HTTP 400 `recipient_duplicate_message` or `recipient_frequency_capped` includes error.details.guard_reason, error.details.next_allowed_at and error.feedback. next_allowed_at is null for pending sends or recovery requiring engagement or consent; never schedule a retry from null. Missing or unreadable policy, unclassifiable pending templates, or new marketing media without verified hashes returns 503 `recipient_cadence_policy_unavailable`. Legacy media equality without a retained hash remains unknown and is logged; no migration invents historic media equality. Queued templates are checked again before dispatch. Batch failure exports expose guard_reason and next_allowed_at when the failure receipt is retained. Recorded provider rejections count as attempts. Guard skips create no provider attempt or message-processing charge and do not renew the cooldown. Customers cannot disable or shorten these protections. They do not guarantee delivery or improved sender quality. Batch guard skips are included in recipient_guard_skipped_count and failed_count. Export failures for each recipient and recovery reason.
