> ## Documentation Index
> Fetch the complete documentation index at: https://developer.omni.z-api.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Mensagem customizada

> O modelo aberto: texto livre com variáveis, cabeçalho e botões

export const TemplatePreview = ({header, headerKind = 'text', headerIcon, productLine, body, footer, buttons = [], cards = [], offer, coupon, order, callPermission, caption, height, lang = 'pt'}) => {
  const L = ({
    pt: {
      contact: 'Cliente teste',
      label: 'pré-visualização',
      orderNote: 'exemplo: os valores e os itens vêm no envio',
      item: 'Produto',
      qty: 'Qtd. 1',
      subtotal: 'Subtotal',
      tax: 'Taxas',
      total: 'Total',
      callAsk: 'A empresa pode te ligar?',
      callAllow: 'Permitir chamadas',
      callAllowTemp: 'Permitir por 7 dias',
      money: 'R$ 00,00',
      price: 'R$ preço',
      name: 'nome do produto'
    },
    en: {
      contact: 'Test customer',
      label: 'preview',
      orderNote: 'sample: amounts and items come at send time',
      item: 'Product',
      qty: 'Qty. 1',
      subtotal: 'Subtotal',
      tax: 'Taxes',
      total: 'Total',
      callAsk: 'Can the business call you?',
      callAllow: 'Allow calls',
      callAllowTemp: 'Temporarily allow calls',
      money: '$ 00.00',
      price: '$ price',
      name: 'product name'
    },
    es: {
      contact: 'Cliente de prueba',
      label: 'previsualización',
      orderNote: 'ejemplo: los valores y los ítems vienen en el envío',
      item: 'Producto',
      qty: 'Cant. 1',
      subtotal: 'Subtotal',
      tax: 'Impuestos',
      total: 'Total',
      callAsk: '¿La empresa puede llamarte?',
      callAllow: 'Permitir llamadas',
      callAllowTemp: 'Permitir por 7 días',
      money: '$ 00,00',
      price: '$ precio',
      name: 'nombre del producto'
    }
  })[lang];
  const S = {
    wrap: {
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'flex-start',
      gap: 8,
      margin: '16px 0',
      flexShrink: 0,
      scrollSnapAlign: 'start'
    },
    device: {
      width: 320,
      maxWidth: '100%',
      padding: 10,
      borderRadius: 30,
      background: '#1f201e',
      boxSizing: 'border-box',
      display: 'flex',
      flexDirection: 'column',
      height,
      boxShadow: '0 18px 40px -28px rgb(31 32 30 / .55)'
    },
    speaker: {
      display: 'block',
      width: '22%',
      height: 3,
      margin: '1px auto 6px',
      borderRadius: 999,
      background: 'rgb(255 255 255 / .35)'
    },
    screen: {
      borderRadius: 22,
      overflow: 'hidden',
      background: '#e9e4dc',
      display: 'flex',
      flexDirection: 'column',
      flex: 1,
      minHeight: 0
    },
    bar: {
      display: 'flex',
      alignItems: 'center',
      gap: 9,
      padding: '11px 13px',
      background: '#477722'
    },
    avatar: {
      width: 28,
      height: 28,
      borderRadius: 999,
      flexShrink: 0,
      background: 'rgb(255 255 255 / .22)'
    },
    name: {
      display: 'block',
      fontSize: 12.5,
      fontWeight: 600,
      lineHeight: 1,
      color: '#fff'
    },
    sub: {
      display: 'block',
      marginTop: 4,
      fontSize: 10.5,
      lineHeight: 1,
      color: 'rgb(255 255 255 / .72)'
    },
    chat: {
      padding: '14px 13px',
      minHeight: 230,
      flex: 1,
      overflowY: 'auto'
    },
    bubble: {
      background: '#fff',
      borderRadius: 10,
      borderTopLeftRadius: 2,
      maxWidth: 252,
      boxShadow: '0 1px 1px rgb(0 0 0 / .13)',
      overflow: 'hidden',
      fontSize: 13,
      lineHeight: 1.45,
      color: '#1f201e'
    },
    pad: {
      padding: '8px 10px 6px'
    },
    media: {
      height: 92,
      background: '#f1efe9',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      flexDirection: 'column',
      gap: 3,
      fontSize: 10,
      color: '#8a9183',
      letterSpacing: '.04em',
      textTransform: 'uppercase',
      textAlign: 'center',
      padding: '0 10px',
      lineHeight: 1.35
    },
    mediaSub: {
      textTransform: 'none',
      letterSpacing: 0,
      fontSize: 9.5
    },
    head: {
      fontWeight: 700,
      marginBottom: 4
    },
    foot: {
      fontSize: 11,
      color: '#8a9183',
      marginTop: 6
    },
    time: {
      display: 'block',
      textAlign: 'right',
      fontSize: 10,
      color: '#9aa093',
      marginTop: 2
    },
    btn: {
      borderTop: '1px solid #e7e9e3',
      padding: '8px 10px',
      textAlign: 'center',
      color: '#027eb5',
      fontSize: 12.5,
      fontWeight: 500
    },
    chip: {
      display: 'inline-block',
      background: '#e3eed9',
      color: '#477722',
      borderRadius: 5,
      padding: '2px 7px',
      fontSize: 11,
      fontWeight: 700,
      marginBottom: 6
    },
    invoice: {
      margin: '8px 0 2px',
      padding: 8,
      borderRadius: 8,
      background: '#f6f5f1',
      display: 'flex',
      flexDirection: 'column',
      gap: 6
    },
    invoiceNote: {
      fontSize: 9,
      color: '#9aa093',
      textTransform: 'uppercase',
      letterSpacing: '.04em'
    },
    line: {
      display: 'flex',
      alignItems: 'center',
      gap: 7
    },
    thumb: {
      width: 26,
      height: 26,
      borderRadius: 5,
      flexShrink: 0,
      background: '#fff',
      border: '1px solid #e7e9e3'
    },
    lineName: {
      flex: 1,
      minWidth: 0,
      fontSize: 10.5,
      lineHeight: 1.3
    },
    lineQty: {
      display: 'block',
      fontSize: 9,
      color: '#9aa093'
    },
    lineValue: {
      fontSize: 10.5,
      flexShrink: 0
    },
    totals: {
      display: 'flex',
      flexDirection: 'column',
      gap: 3,
      paddingTop: 5,
      borderTop: '1px solid #e7e9e3'
    },
    totalRow: {
      display: 'flex',
      justifyContent: 'space-between',
      fontSize: 10.5
    },
    callCard: {
      display: 'flex',
      alignItems: 'center',
      gap: 7,
      padding: '8px 10px',
      fontSize: 11.5,
      color: '#1f201e'
    },
    callIcon: {
      width: 20,
      height: 20,
      borderRadius: 999,
      flexShrink: 0,
      background: '#eef0ea'
    },
    row: {
      display: 'flex',
      gap: 7,
      marginTop: 7,
      maxWidth: 252
    },
    card: {
      background: '#fff',
      borderRadius: 10,
      flex: 1,
      minWidth: 0,
      overflow: 'hidden',
      fontSize: 12,
      boxShadow: '0 1px 1px rgb(0 0 0 / .13)'
    },
    cap: {
      fontSize: 11.5,
      color: 'var(--zp-ink-tertiary, #8a9183)',
      width: 320,
      maxWidth: '100%',
      minHeight: 34
    }
  };
  const ICON = {
    catalog: '🛍',
    product: '🛍',
    location: '📍',
    image: '🖼',
    video: '▶'
  };
  const withVars = text => String(text || '').split(/(\{\{[^}]+\}\})/g).map((part, i) => part.startsWith('{{') ? <span key={i} style={{
    background: '#e3eed9',
    color: '#477722',
    borderRadius: 3,
    padding: '0 3px'
  }}>{part}</span> : <span key={i}>{part}</span>);
  const mediaBlock = (label, sub, height) => <div style={height ? {
    ...S.media,
    height
  } : S.media}>
      {headerIcon && <span style={{
    fontSize: 15
  }}>{ICON[headerIcon] || ''}</span>}
      <span>{label}</span>
      {sub && <span style={S.mediaSub}>{sub}</span>}
    </div>;
  return <div style={S.wrap}>
      <div style={S.device}>
        <span aria-hidden="true" style={S.speaker} />

        <div style={S.screen}>
          <div style={S.bar}>
            <span style={S.avatar} />
            <span style={{
    minWidth: 0
  }}>
              <span style={S.name}>{L.contact}</span>
              <span style={S.sub}>{L.label}</span>
            </span>
          </div>

          <div style={S.chat}>
            <div style={S.bubble}>
              {headerKind !== 'text' && headerKind !== 'none' && mediaBlock(headerKind, productLine === true ? `${L.name} · ${L.price}` : productLine)}

              <div style={S.pad}>
                {offer && <div style={S.chip}>{offer}</div>}
                {header && <div style={S.head}>{withVars(header)}</div>}
                {body && <div style={{
    whiteSpace: 'pre-wrap'
  }}>{withVars(body)}</div>}
                {coupon && <div style={{
    ...S.chip,
    marginTop: 8,
    marginBottom: 0
  }}>{coupon}</div>}

                {order && <div style={S.invoice}>
                    <span style={S.invoiceNote}>{L.orderNote}</span>
                    {[1, 2].map(i => <span key={i} style={S.line}>
                        <span style={S.thumb} />
                        <span style={S.lineName}>
                          {L.item} {i}
                          <span style={S.lineQty}>{L.qty}</span>
                        </span>
                        <span style={S.lineValue}>{L.money}</span>
                      </span>)}
                    <span style={S.totals}>
                      {[[L.subtotal, false], [L.tax, false], [L.total, true]].map(([label, strong]) => <span key={label} style={S.totalRow}>
                          <span style={{
    fontWeight: strong ? 600 : 400,
    color: strong ? '#1f201e' : '#6b7264'
  }}>{label}</span>
                          <span style={{
    fontWeight: strong ? 600 : 400
  }}>{L.money}</span>
                        </span>)}
                    </span>
                  </div>}

                {footer && <div style={S.foot}>{footer}</div>}
                <span style={S.time}>10:24</span>
              </div>

              {callPermission && <div style={{
    borderTop: '1px solid #e7e9e3'
  }}>
                  <div style={S.callCard}>
                    <span style={S.callIcon} />
                    <span>{L.callAsk}</span>
                  </div>
                  <div style={S.btn}>{L.callAllow}</div>
                  <div style={S.btn}>{L.callAllowTemp}</div>
                </div>}

              {buttons.map((b, i) => <div key={i} style={S.btn}>{b}</div>)}
            </div>

            {cards.length > 0 && <div style={S.row}>
                {cards.map((c, i) => <div key={i} style={S.card}>
                    <div style={{
    ...S.media,
    height: 64,
    gap: 2
  }}>
                      <span>{c.media || 'imagem'}</span>
                      {c.product && <span style={S.mediaSub}>{L.name} · {L.price}</span>}
                    </div>
                    {c.text && <div style={{
    padding: '6px 8px'
  }}>{c.text}</div>}
                    {(c.buttons || (c.button ? [c.button] : [])).map((b, j) => <div key={j} style={{
    ...S.btn,
    padding: '6px 8px',
    fontSize: 11.5
  }}>{b}</div>)}
                  </div>)}
              </div>}
          </div>
        </div>
      </div>

      {caption && <div style={S.cap}>{caption}</div>}
    </div>;
};

export const PreviewCycle = ({items = [], lang = 'pt', seconds = 6, height = 540}) => {
  const L = ({
    pt: {
      pause: 'passa sozinho, pare o cursor em cima para ler com calma'
    },
    en: {
      pause: 'it cycles on its own; hover to stop and read'
    },
    es: {
      pause: 'pasa solo, deja el cursor encima para leer con calma'
    }
  })[lang];
  const [order, setOrder] = useState(items.map((_, index) => index));
  const [at, setAt] = useState(0);
  const [paused, setPaused] = useState(false);
  const shuffle = () => {
    const next = items.map((_, index) => index);
    for (let i = next.length - 1; i > 0; i -= 1) {
      const j = Math.floor(Math.random() * (i + 1));
      const keep = next[i];
      next[i] = next[j];
      next[j] = keep;
    }
    return next;
  };
  useEffect(() => {
    setOrder(shuffle());
    setAt(0);
  }, []);
  useEffect(() => {
    if (paused || items.length < 2) return undefined;
    const timer = setTimeout(() => {
      setAt(current => {
        const next = current + 1;
        if (next >= items.length) {
          setOrder(shuffle());
          return 0;
        }
        return next;
      });
    }, seconds * 1000);
    return () => clearTimeout(timer);
  }, [at, paused, items.length, seconds]);
  const index = order[at] === undefined ? 0 : order[at];
  const item = items[index] || ({});
  return <div style={{
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'flex-start',
    gap: 10,
    margin: '16px 0'
  }} onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}>
      <style>{'@keyframes zpFade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}'}</style>

      <div key={at} style={{
    animation: 'zpFade .45s ease both'
  }}>
        <TemplatePreview {...item} lang={lang} height={height} />
      </div>

      <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: 6,
    width: 320,
    maxWidth: '100%'
  }}>
        {items.map((_, dot) => <button key={dot} type="button" aria-label={String(dot + 1)} onClick={() => setAt(dot)} style={{
    flex: 1,
    height: 3,
    padding: 0,
    border: 'none',
    borderRadius: 999,
    cursor: 'pointer',
    background: dot === at ? '#477722' : 'var(--zp-border, #d8dcd4)'
  }} />)}
      </div>

      <span style={{
    fontSize: 11,
    color: 'var(--zp-ink-tertiary, #8a9183)'
  }}>{L.pause}</span>
    </div>;
};

`custom`

Texto livre com variáveis, cabeçalho e botões. É o modelo mais usado e **o único totalmente aberto**: nada aqui é escrito pela Meta, e nenhum campo depende de catálogo, pagamento ou formulário.

## Nove ideias para você partir de uma

export const IDEIAS = [{
  headerKind: "imagem da campanha",
  headerIcon: "image",
  body: "🔥 *A pré-venda foi prorrogada*\nCorra para garantir seu lugar antes que o preço suba.\n\nAcaba às 23h59: depois disso, o mesmo ingresso custa mais caro.",
  footer: "Não quer receber por aqui? Digite #SAIR",
  buttons: ['QUERO MEU INGRESSO'],
  caption: "Promoção · marketing · imagem, negrito, parágrafos e um botão de link"
}, {
  body: "{{nome_do_cliente}}, você deixou {{quantidade}} itens no carrinho. Eles ainda estão reservados por 24 horas.",
  buttons: ['Finalizar compra', 'Não tenho interesse'],
  caption: "Recuperação de carrinho · marketing · link mais uma saída em resposta rápida"
}, {
  headerKind: "vídeo de boas-vindas",
  headerIcon: "video",
  body: "Bem-vindo, {{nome_do_cliente}}! Em 40 segundos você vê como tirar o máximo da sua conta. Por onde quer começar?",
  buttons: ['Configurar agora', 'Falar com alguém'],
  caption: "Boas-vindas · utility · cabeçalho de vídeo e duas respostas rápidas"
}, {
  body: "{{nome_do_cliente}}, seu horário é amanhã às {{hora}} com {{profissional}}. Podemos confirmar?",
  buttons: ['Confirmar', 'Remarcar'],
  caption: "Lembrete de agendamento · utility · só respostas rápidas, sem cabeçalho nem rodapé"
}, {
  headerKind: "documento em PDF",
  headerIcon: "document",
  body: "Sua fatura de {{mes_referencia}} está disponível. Valor de {{valor}}, vencimento em {{data_vencimento}}.",
  footer: "Dúvidas? Fale com a gente",
  buttons: ['Pagar agora', 'Falar com o financeiro'],
  caption: "Cobrança · utility · documento em anexo, link com variável e telefone"
}, {
  headerKind: "endereço e mapa, definidos no envio",
  headerIcon: "location",
  body: "Sua retirada está pronta, {{nome_do_cliente}}. Estamos te esperando até as {{hora_limite}} de hoje.",
  buttons: ['Estou a caminho'],
  caption: "Retirada na loja · utility · cabeçalho de localização, que vira um mapa"
}, {
  header: "Manutenção programada",
  body: "No sábado, das 2h às 5h, o sistema ficará indisponível para manutenção. Nada que você já enviou será perdido.",
  footer: "Equipe de operações",
  caption: "Aviso operacional · utility · sem botão nenhum: nem todo template precisa de ação"
}, {
  body: "Olá! Sua experiência vale muito para a gente 🔥 Conte como foi em menos de 1 minuto.",
  buttons: ['Quero responder!'],
  caption: "Pesquisa · marketing · o mínimo possível: mensagem e um botão"
}, {
  header: "Pedido {{numero_do_pedido}} confirmado",
  body: "Olá {{nome_do_cliente}}, seu pedido {{numero_do_pedido}} foi confirmado e sai para entrega hoje até as 18h.",
  footer: "Responda SAIR para não receber mais",
  buttons: ['Acompanhar pedido', 'Ver detalhes', 'Falar com a loja'],
  caption: "Confirmação de pedido · utility · o payload completo desta página, com os três tipos de botão"
}];

<PreviewCycle lang="pt" items={IDEIAS} />

<Info>
  Arraste a faixa para o lado. Estes nove são **pontos de partida, não um catálogo**: todos são o mesmo `custom`, e o que muda é quais componentes entram. Você combina os que quiser — cabeçalho de qualquer formato, ou nenhum; rodapé, ou nenhum; até dez botões, ou nenhum.
</Info>

Abra o exemplo que se parece com o que você quer e **copie o JSON** pelo botão no canto do bloco. Ele cria exatamente o template da prévia; a partir dele, troque texto, variáveis e botões.

<AccordionGroup>
  <Accordion title="1. Promoção com imagem">
    <TemplatePreview {...IDEIAS[0]} lang="pt" />

    ```json theme={null}
    {
      "name": "prevenda_prorrogada",
      "category": "MARKETING",
      "language": "pt_BR",
      "allow_category_change": true,
      "components": [
        {
          "type": "HEADER",
          "format": "IMAGE",
          "example": { "header_handle": ["4::aW1hZ2UvcG5n..."] }
        },
        {
          "type": "BODY",
          "text": "🔥 *A pré-venda foi prorrogada*\nCorra para garantir seu lugar antes que o preço suba.\n\nAcaba às 23h59: depois disso, o mesmo ingresso custa mais caro."
        },
        { "type": "FOOTER", "text": "Não quer receber por aqui? Digite #SAIR" },
        {
          "type": "BUTTONS",
          "buttons": [
            { "type": "URL", "text": "QUERO MEU INGRESSO", "url": "https://sualoja.com/ingresso" }
          ]
        }
      ]
    }
    ```

    Sem variável, não há `example` no corpo. O negrito é o `*asterisco*` do WhatsApp, e os parágrafos são `\n` — quebra de linha só a mensagem aceita. No envio, cabeçalho de mídia **exige** o parâmetro da imagem: a da criação é só a amostra que a Meta revisa.
  </Accordion>

  <Accordion title="2. Recuperação de carrinho">
    <TemplatePreview {...IDEIAS[1]} lang="pt" />

    ```json theme={null}
    {
      "name": "carrinho_abandonado",
      "category": "MARKETING",
      "language": "pt_BR",
      "allow_category_change": true,
      "parameter_format": "NAMED",
      "components": [
        {
          "type": "BODY",
          "text": "{{nome_do_cliente}}, você deixou {{quantidade}} itens no carrinho. Eles ainda estão reservados por 24 horas.",
          "example": {
            "body_text_named_params": [
              { "param_name": "nome_do_cliente", "example": "Marina" },
              { "param_name": "quantidade", "example": "3" }
            ]
          }
        },
        {
          "type": "BUTTONS",
          "buttons": [
            { "type": "URL", "text": "Finalizar compra", "url": "https://sualoja.com/carrinho" },
            { "type": "QUICK_REPLY", "text": "Não tenho interesse" }
          ]
        }
      ]
    }
    ```

    A resposta rápida vem **depois** do link, e não entre outros tipos: os botões ficam em dois grupos. Dar uma saída explícita reduz bloqueio, que é o que mais derruba a qualidade do número.
  </Accordion>

  <Accordion title="3. Boas-vindas com vídeo">
    <TemplatePreview {...IDEIAS[2]} lang="pt" />

    ```json theme={null}
    {
      "name": "boas_vindas_onboarding",
      "category": "UTILITY",
      "language": "pt_BR",
      "parameter_format": "NAMED",
      "components": [
        {
          "type": "HEADER",
          "format": "VIDEO",
          "example": { "header_handle": ["4::dmlkZW8vbXA0..."] }
        },
        {
          "type": "BODY",
          "text": "Bem-vindo, {{nome_do_cliente}}! Em 40 segundos você vê como tirar o máximo da sua conta. Por onde quer começar?",
          "example": {
            "body_text_named_params": [{ "param_name": "nome_do_cliente", "example": "Marina" }]
          }
        },
        {
          "type": "BUTTONS",
          "buttons": [
            { "type": "QUICK_REPLY", "text": "Configurar agora" },
            { "type": "QUICK_REPLY", "text": "Falar com alguém" }
          ]
        }
      ]
    }
    ```

    Vídeo no cabeçalho aceita MP4 e 3GPP, até 16 MB. A resposta rápida abre a janela de 24 horas quando o cliente toca, e aí você conversa sem template.
  </Accordion>

  <Accordion title="4. Lembrete de agendamento">
    <TemplatePreview {...IDEIAS[3]} lang="pt" />

    ```json theme={null}
    {
      "name": "lembrete_agendamento",
      "category": "UTILITY",
      "language": "pt_BR",
      "parameter_format": "NAMED",
      "components": [
        {
          "type": "BODY",
          "text": "{{nome_do_cliente}}, seu horário é amanhã às {{hora}} com {{profissional}}. Podemos confirmar?",
          "example": {
            "body_text_named_params": [
              { "param_name": "nome_do_cliente", "example": "Marina" },
              { "param_name": "hora", "example": "14h30" },
              { "param_name": "profissional", "example": "Dra. Helena" }
            ]
          }
        },
        {
          "type": "BUTTONS",
          "buttons": [
            { "type": "QUICK_REPLY", "text": "Confirmar" },
            { "type": "QUICK_REPLY", "text": "Remarcar" }
          ]
        }
      ]
    }
    ```

    A resposta do cliente volta no seu webhook com o texto do botão. Repare que a variável não abre nem fecha o texto — se abrisse, a Meta recusaria com `2388299`.
  </Accordion>

  <Accordion title="5. Cobrança com boleto em PDF">
    <TemplatePreview {...IDEIAS[4]} lang="pt" />

    ```json theme={null}
    {
      "name": "fatura_disponivel",
      "category": "UTILITY",
      "language": "pt_BR",
      "parameter_format": "NAMED",
      "components": [
        {
          "type": "HEADER",
          "format": "DOCUMENT",
          "example": { "header_handle": ["4::YXBwbGljYXRpb24vcGRm..."] }
        },
        {
          "type": "BODY",
          "text": "Sua fatura de {{mes_referencia}} está disponível. Valor de {{valor}}, vencimento em {{data_vencimento}}.",
          "example": {
            "body_text_named_params": [
              { "param_name": "mes_referencia", "example": "setembro" },
              { "param_name": "valor", "example": "R$ 249,90" },
              { "param_name": "data_vencimento", "example": "10/10" }
            ]
          }
        },
        { "type": "FOOTER", "text": "Dúvidas? Fale com a gente" },
        {
          "type": "BUTTONS",
          "buttons": [
            {
              "type": "URL",
              "text": "Pagar agora",
              "url": "https://sualoja.com/fatura/{{1}}",
              "example": ["https://sualoja.com/fatura/1042"]
            },
            { "type": "PHONE_NUMBER", "text": "Falar com o financeiro", "phone_number": "+5511988881234" }
          ]
        }
      ]
    }
    ```

    O botão de link aceita **uma** variável e só no fim do endereço. Ela é posicional mesmo em template nomeado, porque é a numeração do próprio botão.
  </Accordion>

  <Accordion title="6. Retirada na loja, com mapa">
    <TemplatePreview {...IDEIAS[5]} lang="pt" />

    ```json theme={null}
    {
      "name": "retirada_pronta",
      "category": "UTILITY",
      "language": "pt_BR",
      "parameter_format": "NAMED",
      "components": [
        { "type": "HEADER", "format": "LOCATION" },
        {
          "type": "BODY",
          "text": "Sua retirada está pronta, {{nome_do_cliente}}. Estamos te esperando até as {{hora_limite}} de hoje.",
          "example": {
            "body_text_named_params": [
              { "param_name": "nome_do_cliente", "example": "Marina" },
              { "param_name": "hora_limite", "example": "18h" }
            ]
          }
        },
        {
          "type": "BUTTONS",
          "buttons": [{ "type": "QUICK_REPLY", "text": "Estou a caminho" }]
        }
      ]
    }
    ```

    O cabeçalho de localização **não tem nada para preencher na criação**: o endereço, o nome do lugar e as coordenadas vão no envio. Serve para mandar a mesma mensagem apontando para lojas diferentes.
  </Accordion>

  <Accordion title="7. Aviso operacional, sem botão">
    <TemplatePreview {...IDEIAS[6]} lang="pt" />

    ```json theme={null}
    {
      "name": "manutencao_programada",
      "category": "UTILITY",
      "language": "pt_BR",
      "components": [
        { "type": "HEADER", "format": "TEXT", "text": "Manutenção programada" },
        {
          "type": "BODY",
          "text": "No sábado, das 2h às 5h, o sistema ficará indisponível para manutenção. Nada que você já enviou será perdido."
        },
        { "type": "FOOTER", "text": "Equipe de operações" }
      ]
    }
    ```

    Nem todo template precisa de ação. Sem variável e sem botão, este é o formato mais barato de aprovar e o mais difícil de ser recusado.
  </Accordion>

  <Accordion title="8. Pesquisa de satisfação">
    <TemplatePreview {...IDEIAS[7]} lang="pt" />

    ```json theme={null}
    {
      "name": "pesquisa_experiencia",
      "category": "MARKETING",
      "language": "pt_BR",
      "allow_category_change": true,
      "components": [
        {
          "type": "BODY",
          "text": "Olá! Sua experiência vale muito para a gente 🔥 Conte como foi em menos de 1 minuto."
        },
        {
          "type": "BUTTONS",
          "buttons": [
            { "type": "URL", "text": "Quero responder!", "url": "https://sualoja.com/pesquisa" }
          ]
        }
      ]
    }
    ```

    O mínimo que um template precisa: mensagem e nada mais. Sem variável e com link fixo, o envio leva só o nome e o idioma.
  </Accordion>

  <Accordion title="9. Confirmação de pedido">
    <TemplatePreview {...IDEIAS[8]} lang="pt" />

    É o payload completo desta página: cabeçalho de texto com variável, mensagem, rodapé e os três tipos de botão livre. Ele está inteiro no [JSON de criação](#1-criar-o-template), com o JSON de envio ao lado.
  </Accordion>
</AccordionGroup>

## Como funciona

Você escreve o texto uma vez, marca com variável o que muda a cada cliente, e a Meta aprova o molde. No envio, cada variável recebe o valor daquele disparo.

É o modelo para tudo que não é comércio, autenticação nem promoção com estrutura própria: confirmação de pedido, aviso de status, cobrança, lembrete de agendamento, retomada de contato.

## Na criação e no envio

| Na criação                                    | No envio                                                        |
| --------------------------------------------- | --------------------------------------------------------------- |
| O texto do cabeçalho, da mensagem e do rodapé | O valor de cada variável                                        |
| Os botões e para onde eles levam              | O trecho final do endereço, quando o botão de link tem variável |
| A amostra da mídia do cabeçalho               | O arquivo real daquele disparo                                  |

## Campos deste modelo

| Campo      | Para que serve                                                                  | Regra                                                                       |
| ---------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `category` | `UTILITY` ou `MARKETING`, conforme o conteúdo                                   | Você escolhe. Com `allow_category_change`, a Meta corrige em vez de recusar |
| `HEADER`   | Opcional. Texto, imagem, vídeo, documento ou mapa                               | Texto até 60 caracteres e no máximo uma variável                            |
| `BODY`     | O texto principal. **Obrigatório**                                              | Até 1024 caracteres, com quebra de linha                                    |
| `FOOTER`   | Opcional. Aviso discreto abaixo da mensagem                                     | Até 60 caracteres, sem variável                                             |
| `BUTTONS`  | Opcional. Resposta rápida, link, telefone, código, chamada de voz ou formulário | Até 10, com as respostas rápidas agrupadas                                  |

## JSON completo

São dois JSON: um **cria** o template, e a Meta aprova uma vez. O outro **envia** a mensagem, e vai a cada disparo, com os valores daquele envio nas variáveis marcadas com chaves.

### 1. Criar o template

`POST /whatsapp/businesses/{wabaId}/templates` — ver em [Referência da API](/templates/create-template).

```json theme={null}
{
  "name": "exemplo_custom",
  "category": "UTILITY",
  "language": "pt_BR",
  "allow_category_change": true,
  "parameter_format": "NAMED",
  "components": [
    {
      "type": "HEADER",
      "format": "TEXT",
      "text": "Pedido {{numero_do_pedido}}",
      "example": {
        "header_text_named_params": [
          {
            "param_name": "numero_do_pedido",
            "example": "1042"
          }
        ]
      }
    },
    {
      "type": "BODY",
      "text": "Olá {{nome_do_cliente}}, o seu pedido {{numero_do_pedido}} foi confirmado e sai para entrega hoje.",
      "example": {
        "body_text_named_params": [
          {
            "param_name": "nome_do_cliente",
            "example": "Marina"
          },
          {
            "param_name": "numero_do_pedido",
            "example": "1042"
          }
        ]
      }
    },
    {
      "type": "FOOTER",
      "text": "Responda SAIR para não receber mais"
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "QUICK_REPLY",
          "text": "Acompanhar pedido"
        },
        {
          "type": "URL",
          "text": "Ver detalhes",
          "url": "https://z-api.io/pedido/{{numero_do_pedido}}",
          "example": [
            "https://z-api.io/pedido/1042"
          ]
        },
        {
          "type": "PHONE_NUMBER",
          "text": "Falar com a loja",
          "phone_number": "+5511988881234"
        },
        {
          "type": "COPY_CODE",
          "example": "PROMO10"
        },
        {
          "type": "VOICE_CALL",
          "text": "Falar por voz"
        }
      ]
    }
  ]
}
```

### 2. Enviar depois de aprovado

`POST /v1/channels/{channelId}/messages` — ver em [Enviar template](/messages/send-template).

```json theme={null}
{
  "recipient": {
    "identifier": "5511999999999"
  },
  "content": {
    "type": "TEMPLATE",
    "attachments": [
      {
        "template": {
          "name": "exemplo_custom",
          "language": {
            "policy": "deterministic",
            "code": "pt_BR"
          },
          "components": [
            {
              "type": "header",
              "parameters": [
                {
                  "type": "text",
                  "text": "{{numero_do_pedido}}"
                }
              ]
            },
            {
              "type": "body",
              "parameters": [
                {
                  "type": "text",
                  "text": "{{nome_do_cliente}}"
                },
                {
                  "type": "text",
                  "text": "{{numero_do_pedido}}"
                }
              ]
            },
            {
              "type": "button",
              "sub_type": "quick_reply",
              "index": 0,
              "parameters": [
                {
                  "type": "payload",
                  "payload": "{{quick_reply_payload_1}}"
                }
              ]
            },
            {
              "type": "button",
              "sub_type": "url",
              "index": 1,
              "parameters": [
                {
                  "type": "text",
                  "text": "{{url_suffix_2}}"
                }
              ]
            },
            {
              "type": "button",
              "sub_type": "phone_number",
              "index": 2,
              "parameters": []
            },
            {
              "type": "button",
              "sub_type": "copy_code",
              "index": 3
            },
            {
              "type": "button",
              "sub_type": "voice_call",
              "index": 4
            },
            {
              "type": "button",
              "sub_type": "flow",
              "index": 5,
              "parameters": [
                {
                  "type": "action",
                  "action": {
                    "flow_token": "{{flow_token}}",
                    "flow_action_data": {}
                  }
                }
              ]
            }
          ]
        }
      }
    ]
  }
}
```

## O que a Meta recusa aqui

* Variável **abrindo ou fechando** o texto do cabeçalho ou da mensagem.
* Variável **sem exemplo**.
* Texto curto com muitas variáveis.
* Resposta rápida no meio dos outros botões.
* Endereço de botão sem `http` ou `https`, e telefone sem o código do país.

Os códigos de erro de cada caso estão em [Validações](/templates/validation).
