Criar template manualmente no painel da Meta nao escala nem audita. A alternativa e tratar template como codigo: a definicao vive em um repositorio versionado, passa por pull request (a revisao de copy e a checagem de categoria viram revisao de PR) e e sincronizada para a Meta via Graph API. O painel deixa de ser a fonte da verdade; o repositorio passa a ser. Cada mudanca tem autor, diff, historico e rollback.
# templates/support_orderupdate_pt_v2.yaml
# A definicao versionada e a fonte da verdade. O painel da Meta
# e apenas um reflexo deste arquivo, sincronizado via Graph API.
name: support_orderupdate_pt_v2
language: pt_BR
category: UTILITY # utility: atualizacao ligada a acao do usuario
owner: support
components:
- type: BODY
text: "Ola {{1}}, seu pedido {{2}} mudou para o status: {{3}}."
example:
body_text:
- ["Joao", "#10482", "enviado"]
- type: BUTTONS
buttons:
- type: URL
text: "Acompanhar pedido"
url: "https://exemplo.com/pedidos/{{1}}"
example: ["https://exemplo.com/pedidos/10482"]
A sincronizacao le o arquivo e cria ou atualiza o template no WABA via Graph API. O mesmo script roda em CI: ao mergear o PR, o template e submetido a Meta e o pipeline registra o status retornado.
// sync-template.js
// Le a definicao YAML e submete o template a Meta via Graph API.
// Rode no CI apos o merge do PR que aprovou a copy e a categoria.
import fs from 'node:fs';
import yaml from 'js-yaml';
const WABA_ID = process.env.WABA_ID;
const TOKEN = process.env.META_TOKEN;
async function syncTemplate(path) {
const def = yaml.load(fs.readFileSync(path, 'utf8'));
const payload = {
name: def.name,
language: def.language,
category: def.category,
components: def.components,
};
const res = await fetch(
`https://graph.facebook.com/v21.0/${WABA_ID}/message_templates`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
},
);
const data = await res.json();
if (!res.ok) {
// status de rejeicao da Meta volta aqui: logue e falhe o pipeline
throw new Error(`Falha ao sincronizar ${def.name}: ${JSON.stringify(data)}`);
}
// data.status costuma ser PENDING ate a Meta analisar
console.log(`Submetido ${def.name}: status ${data.status}`);
return data;
}
syncTemplate(process.argv[2]).catch((err) => {
console.error(err);
process.exit(1);
});
Com essa base, todo o processo de aprovacao acontece na revisao do PR e a Meta recebe apenas o que ja passou pelos portoes. O namespace fica auditavel: para saber por que um template existe, quem o criou e o que mudou entre versoes, basta olhar o historico do repositorio.