Submit a template version for review
/v1/whatsapp/templates/{template_ref}/versions/{version_id}/submitconst draftId = "wav_01krdgeqcxet5s7t44vh8rt9mg";
const check = await bird.whatsapp.templates.versions.submit("order_update", draftId, {
validate_only: true,
});
for (const problem of check.errors) console.log(problem.language, problem.field, problem.message);
if (check.valid) {
const submitted = await bird.whatsapp.templates.versions.submit("order_update", draftId);
console.log(submitted.version?.id);
}draft_id = "wav_01krdgeqcxet5s7t44vh8rt9mg"
check = client.whatsapp.templates.versions.submit("order_update", draft_id, validate_only=True)
for problem in check.errors:
print(problem.language, problem.field, problem.message)
if check.valid:
submitted = client.whatsapp.templates.versions.submit("order_update", draft_id)
print(submitted.version.id if submitted.version else None)ctx := context.Background()
draftID := "wav_01krdgeqcxet5s7t44vh8rt9mg"
check, err := client.Whatsapp.Templates.Versions.Submit(ctx, "order_update", draftID,
bird.WhatsappTemplatesVersionsSubmitParams{ValidateOnly: bird.Bool(true)})
if err != nil {
log.Fatal(err)
}
if check.Errors != nil {
for _, problem := range *check.Errors {
fmt.Println(*problem.Message)
}
}
if check.Valid != nil && *check.Valid {
submitted, err := client.Whatsapp.Templates.Versions.Submit(ctx, "order_update", draftID,
bird.WhatsappTemplatesVersionsSubmitParams{})
if err != nil {
log.Fatal(err)
}
if submitted.Version != nil {
fmt.Println(submitted.Version.Id)
}
}$draftId = 'wav_01krdgeqcxet5s7t44vh8rt9mg';
$check = $bird->whatsapp->templates->versions->submit('order_update', $draftId, (new WhatsAppTemplateSubmit())->setValidateOnly(true));
foreach ($check->getErrors() ?? [] as $problem) {
echo $problem->getLanguage(), ' ', $problem->getField(), ' ', $problem->getMessage(), "\n";
}
if ($check->getValid()) {
$submitted = $bird->whatsapp->templates->versions->submit('order_update', $draftId, new WhatsAppTemplateSubmit());
echo $submitted->getVersion()?->getId(), "\n";
}bird whatsapp templates versions submit <template-ref> <version-id> \
--validate-only=truecurl -X POST "https://us1.platform.bird.com/v1/whatsapp/templates/{template_ref}/versions/{version_id}/submit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"validate_only": true
}'{
"errors": [
{
"language": "pt-BR",
"field": "components[2].cards[0].components[1].example_parameters[0].url",
"code": "E15025",
"message": "A body block needs text."
}
],
"version": {
"id": "wav_01krdgeqcxet5s7t44vh8rt9mg",
"template_id": "wat_01krdgeqcxet5s7t44vh8rt9mg",
"version_number": 4,
"submitted_at": "2026-07-20T11:04:00Z",
"variables": [
{
"type": "code"
}
],
"languages": {
"en": {
"status": "approved",
"components": [
{
"type": "body",
"text": "Your verification code is {{1}}.",
"example_parameters": [
{
"type": "text",
"text": "123456"
}
]
}
]
}
},
"created_at": "2026-07-20T10:31:00Z"
},
"next": [
{
"kind": "operation"
}
]
}
Validates the template draft before freezing it. If validation fails, the
API returns 200 OK with every problem across all languages and creates no
version. If validation passes, the API freezes an immutable version and
returns 202 Accepted. Only the languages whose content differs from their
approved copy are sent to Meta and marked pending; the rest carry their
existing verdict forward untouched, so a version whose languages are all
unchanged comes back already settled. A language an earlier version carried
but the draft dropped is deleted at Meta in the background. It is absent
from the new version, so it has no pending state. The version becomes live when any
language is approved, and no replacement draft opens automatically.
Set validate_only: true to run the same checks without freezing or
submitting a version. Local validation cannot predict Meta's decision. A
202 Accepted response confirms the version was frozen and accepted. Poll the
version or the template's pending_version_id for the verdicts on whichever
languages it marked pending.
Paramètres
template_refstringTemplate ID (wat_ prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.
version_idstringID of the template version (wav_ prefix), as returned by the version list.
Corps de la requête
validate_onlyCheck the draft without submitting it. Every language is checked and every problem reported, but nothing is frozen and nothing is sent to WhatsApp. A clean result does not guarantee WhatsApp approval: WhatsApp offers no way to ask in advance, so it can still refuse a submission these checks passed. Use a different idempotency key for the validation and the submission. Reusing one key with different request bodies is rejected.
Contenu de la réponse
validWhether the version passed every check.
errorsEvery problem found across every language. One round trip identifies all problems. Empty when valid is true.
versionThe version this submit froze, or null when nothing was frozen: a check run, or a submit that found problems.
Afficher les attributs enfants
version.idStable Bird identifier for the version.
version.template_idThe template this version belongs to.
version.version_numberThe version's sequence number, assigned when it is submitted. Null on a draft, which has not been submitted and has no place in the sequence yet.
version.submitted_atWhen this version was submitted to Meta. Null on a draft, which has not been submitted, and null for a built-in template's version, which Bird ships already approved rather than submitting on your behalf.
version.variablesThe variable slots this version's content fills in from the values you supply when sending. Combined across every language, because languages need not declare the same slots: if the English body uses first_name and the French one uses last_name, both appear here. A template with named parameters keys them by name; a positional one keys them by their {{n}} position, as 1, 2 and so on. Supply the slots the language you send declares, by these keys in template.parameters or through components.
Afficher les attributs enfants
version.variables.keyThe key this slot is filled by. When system is true it is the reserved bird key or a dotted path beneath it, such as bird.contact.first_name, and naming it in a send is rejected. Otherwise, on email and SMS it is the key you set in the send's parameters object, and on WhatsApp the key in the send's template.parameters, or the name on the matching parameter inside components. A WhatsApp template whose placeholders are positional uses the position itself as 1, 2 and so on, and values with no placeholder use fixed keys: header_url, header_filename, header_location and code, plus button_url (or button_url_1, button_url_2 for a carousel card) for a url button whose suffix is numbered.
version.variables.typeThe value type this slot accepts. Each typed slot rejects a value that does not match its constraint. Built-in SMS templates use typed slots (code, amount and the rest); domain is a lowercase DNS domain name such as example.com, which the verification templates fill to bind a passcode to a website, and app_hash is an 11-character Android SMS Retriever app hash such as FA+9qCX9VSu, which they fill to bind it to an Android app. Apple Messages templates type a variable from its content: the field it fills (url, image, timestamp, location, timezone) or the formatting filter applied to it (date, date_time, amount, currency). Email and workspace SMS templates use text. WhatsApp templates use text, plus url for a media header's file and location for a location header. A location value is an object with latitude and longitude, and optionally name and address. On Apple Messages, name becomes the location's title and address is not shown. Workspace SMS parameters must be scalar values.
Possible values (may grow over time): code, ttl, count, ref, date, date_time, amount, currency, domain, app_hash, url, image, timestamp, timezone, location, text
version.variables.requiredWhether the send must supply this variable. Omitting a required value returns 422 on every channel. On Apple Messages, a later time slot's start that is used nowhere else is optional: a send that omits it drops that slot. Always false when system is true, because you do not supply that slot's value.
version.variables.constraintA plain-language description of what values this variable accepts. When system is true it names where Bird takes the value from instead, because there is no value for you to send.
version.variables.sensitiveWhether this slot requires redacted message history. Published email template sends that store recipient snapshots reject sensitive inputs before acceptance. Other channels retain their own sensitive-input delivery and retention rules.
version.variables.systemWhether the value comes from Bird rather than from the send. Absent means false. System slots are identified by the reserved bird key or a dotted path beneath it. They appear on email templates, and on WhatsApp templates whose parameter bindings read contact data; every SMS slot is yours to fill. A draft can also name a reserved key no Bird value fills, including bird itself: constraint says so, and publishing that draft is rejected.
version.languagesThis version's content, keyed by BCP-47 language tag, with what its submission did with each language.
version.created_atWhen the version was opened. Null for a built-in template's version, which Bird ships rather than stores.
nextWhat to do next with this outcome. Present on reads that compute it: an empty list means there is nothing to do, and the field is absent entirely on responses that do not report next actions.
A failed check routes back to the languages that need an edit; a clean one routes to the real submit, and then to polling the template for WhatsApp's verdicts.
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet.