Create Template
const url = 'https://example.com/platform/v1/templates';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"body_text":"example","category":"utility","channel_id":1,"components":[{"example":{"header_handle":["example"],"header_text":["example"]},"format":"TEXT","text":"example","type":"HEADER"}],"language_code":"es","name":"example","parameter_format":"NAMED","variables":["example"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/platform/v1/templates \ --header 'Content-Type: application/json' \ --data '{ "body_text": "example", "category": "utility", "channel_id": 1, "components": [ { "example": { "header_handle": [ "example" ], "header_text": [ "example" ] }, "format": "TEXT", "text": "example", "type": "HEADER" } ], "language_code": "es", "name": "example", "parameter_format": "NAMED", "variables": [ "example" ] }'Create a platform template and submit it to Meta.
The body accepts EITHER the legacy body_text + variables (body-only
fast path, byte-identical to WF3 templates) OR the full Meta components
array + optional parameter_format. The Pydantic validator enforces
cross-component consistency; the service raises ValueError for
component-shape errors / missing or non-owned channel / IG channel /
inactive channel — all map to 422.
Request Body required
Section titled “Request Body required ”Body for POST /platform/v1/templates.
Two valid shapes (exclusive):
- Legacy body-only (byte-identical to WF3 templates) — supply
body_textand optionallyvariables; omitcomponents. - Full component surface (WF6) — supply
componentsarray and optionalparameter_format.body_textis then ignored at submit time (the BODY component carries the text).
Authentication category REQUIRES the components path (the fixed body + OTP
button cannot be expressed via body_text alone).
object
HEADER — one per template/card. format drives the invariants.
TEXT requires text; IMAGE/VIDEO/DOCUMENT require
example.header_handle; LOCATION carries neither. Enforced by
build_template_components._build_header.
object
HEADER example — header_text (TEXT) XOR header_handle (media).
header_handle is the list-of-one Meta upload handle returned by
POST /platform/v1/templates/header-media.
object
BODY — exactly one per template/card scope.
text is required for utility/marketing and FORBIDDEN for
authentication (Meta renders the OTP copy), so the requirement is enforced
category-aware in the builder, not here.
add_security_recommendation is the authentication-only flag.
object
BODY example — POSITIONAL body_text XOR NAMED
body_text_named_params.
POSITIONAL is Meta’s list-of-one-list: {"body_text": [["Ada", "A-123"]]}.
object
One parameter_format=NAMED body placeholder (companion §2.2).
object
FOOTER — at most one.
Regular templates carry text; authentication templates carry
code_expiration_minutes (1..90) instead, per Meta.
BUTTONS — non-empty; per-type count rules stay in the builder.
object
QUICK_REPLY — a label the recipient taps to send back a payload.
object
URL — static or Meta-supported dynamic ({{1}}) link.
example is Meta’s list of fully-resolved example URLs (dynamic only).
object
PHONE_NUMBER — tap-to-call.
object
COPY_CODE — coupon copy button. example is the code STRING.
Never treated as a URL button. text is optional because the built
array Meta receives carries only type + example.
OTP — authentication-category button (COPY_CODE/ONE_TAP/ZERO_TAP).
supported_apps is the field the builder actually consumes for
ONE_TAP/ZERO_TAP. package_name / signature_hash / autofill_text
are accepted as the companion documents them (Meta’s flat single-app form)
and are passed through untouched; the builder ignores them.
object
One ONE_TAP/ZERO_TAP target app (passthrough — Meta owns the values).
object
CATALOG — opens the business catalog (passthrough).
CAROUSEL — top-level only, 2..10 cards, never nested.
object
One carousel card — its own typed component sub-tree, order preserved.
object
HEADER — one per template/card. format drives the invariants.
TEXT requires text; IMAGE/VIDEO/DOCUMENT require
example.header_handle; LOCATION carries neither. Enforced by
build_template_components._build_header.
object
HEADER example — header_text (TEXT) XOR header_handle (media).
header_handle is the list-of-one Meta upload handle returned by
POST /platform/v1/templates/header-media.
object
BODY — exactly one per template/card scope.
text is required for utility/marketing and FORBIDDEN for
authentication (Meta renders the OTP copy), so the requirement is enforced
category-aware in the builder, not here.
add_security_recommendation is the authentication-only flag.
object
BODY example — POSITIONAL body_text XOR NAMED
body_text_named_params.
POSITIONAL is Meta’s list-of-one-list: {"body_text": [["Ada", "A-123"]]}.
object
One parameter_format=NAMED body placeholder (companion §2.2).
object
BUTTONS — non-empty; per-type count rules stay in the builder.
object
QUICK_REPLY — a label the recipient taps to send back a payload.
object
URL — static or Meta-supported dynamic ({{1}}) link.
example is Meta’s list of fully-resolved example URLs (dynamic only).
object
PHONE_NUMBER — tap-to-call.
object
COPY_CODE — coupon copy button. example is the code STRING.
Never treated as a URL button. text is optional because the built
array Meta receives carries only type + example.
OTP — authentication-category button (COPY_CODE/ONE_TAP/ZERO_TAP).
supported_apps is the field the builder actually consumes for
ONE_TAP/ZERO_TAP. package_name / signature_hash / autofill_text
are accepted as the companion documents them (Meta’s flat single-app form)
and are passed through untouched; the builder ignores them.
object
One ONE_TAP/ZERO_TAP target app (passthrough — Meta owns the values).
object
CATALOG — opens the business catalog (passthrough).
Responses
Section titled “ Responses ”Successful Response
Response shape — exposes Meta status snapshot + WF6 component fields.
object
HEADER — one per template/card. format drives the invariants.
TEXT requires text; IMAGE/VIDEO/DOCUMENT require
example.header_handle; LOCATION carries neither. Enforced by
build_template_components._build_header.
object
HEADER example — header_text (TEXT) XOR header_handle (media).
header_handle is the list-of-one Meta upload handle returned by
POST /platform/v1/templates/header-media.
object
BODY — exactly one per template/card scope.
text is required for utility/marketing and FORBIDDEN for
authentication (Meta renders the OTP copy), so the requirement is enforced
category-aware in the builder, not here.
add_security_recommendation is the authentication-only flag.
object
BODY example — POSITIONAL body_text XOR NAMED
body_text_named_params.
POSITIONAL is Meta’s list-of-one-list: {"body_text": [["Ada", "A-123"]]}.
object
One parameter_format=NAMED body placeholder (companion §2.2).
object
FOOTER — at most one.
Regular templates carry text; authentication templates carry
code_expiration_minutes (1..90) instead, per Meta.
BUTTONS — non-empty; per-type count rules stay in the builder.
object
QUICK_REPLY — a label the recipient taps to send back a payload.
object
URL — static or Meta-supported dynamic ({{1}}) link.
example is Meta’s list of fully-resolved example URLs (dynamic only).
object
PHONE_NUMBER — tap-to-call.
object
COPY_CODE — coupon copy button. example is the code STRING.
Never treated as a URL button. text is optional because the built
array Meta receives carries only type + example.
OTP — authentication-category button (COPY_CODE/ONE_TAP/ZERO_TAP).
supported_apps is the field the builder actually consumes for
ONE_TAP/ZERO_TAP. package_name / signature_hash / autofill_text
are accepted as the companion documents them (Meta’s flat single-app form)
and are passed through untouched; the builder ignores them.
object
One ONE_TAP/ZERO_TAP target app (passthrough — Meta owns the values).
object
CATALOG — opens the business catalog (passthrough).
Read-path CAROUSEL — cards use the tolerant card union.
object
Read-path carousel card — tolerates unmodelled card components.
object
HEADER — one per template/card. format drives the invariants.
TEXT requires text; IMAGE/VIDEO/DOCUMENT require
example.header_handle; LOCATION carries neither. Enforced by
build_template_components._build_header.
object
HEADER example — header_text (TEXT) XOR header_handle (media).
header_handle is the list-of-one Meta upload handle returned by
POST /platform/v1/templates/header-media.
object
BODY — exactly one per template/card scope.
text is required for utility/marketing and FORBIDDEN for
authentication (Meta renders the OTP copy), so the requirement is enforced
category-aware in the builder, not here.
add_security_recommendation is the authentication-only flag.
object
BODY example — POSITIONAL body_text XOR NAMED
body_text_named_params.
POSITIONAL is Meta’s list-of-one-list: {"body_text": [["Ada", "A-123"]]}.
object
One parameter_format=NAMED body placeholder (companion §2.2).
object
BUTTONS — non-empty; per-type count rules stay in the builder.
object
QUICK_REPLY — a label the recipient taps to send back a payload.
object
URL — static or Meta-supported dynamic ({{1}}) link.
example is Meta’s list of fully-resolved example URLs (dynamic only).
object
PHONE_NUMBER — tap-to-call.
object
COPY_CODE — coupon copy button. example is the code STRING.
Never treated as a URL button. text is optional because the built
array Meta receives carries only type + example.
OTP — authentication-category button (COPY_CODE/ONE_TAP/ZERO_TAP).
supported_apps is the field the builder actually consumes for
ONE_TAP/ZERO_TAP. package_name / signature_hash / autofill_text
are accepted as the companion documents them (Meta’s flat single-app form)
and are passed through untouched; the builder ignores them.
object
One ONE_TAP/ZERO_TAP target app (passthrough — Meta owns the values).
object
CATALOG — opens the business catalog (passthrough).
object
object
Example
{ "components": [ { "format": "TEXT", "type": "HEADER" } ]}Validation Error
object
object
object
Example generated
{ "detail": [ { "ctx": {}, "input": "example", "loc": [ "example" ], "msg": "example", "type": "example" } ]}