> ## 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.

# Cupom

> Mensagem com código de desconto que o cliente copia com um toque

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>;
};

`coupon_code`

Mensagem com código de desconto copiável.

<TemplatePreview header="Sua recompensa chegou" body="{{nome_do_cliente}}, você ganhou um cupom de 10% na próxima compra. Toque para copiar o código." coupon="VOLTA10" footer="Válido até o fim do mês" buttons={['Copiar código', 'Ver detalhes', 'Falar com a loja', 'Acompanhar pedido']} caption="Código copiável, link, telefone e resposta rápida — as respostas rápidas agrupadas no fim, como a Meta exige." />

## Como funciona

O botão de copiar código é o coração do modelo: o cliente toca e o código vai para a área de transferência, pronto para colar no seu site ou no seu aplicativo.

O código que você põe na criação é a **amostra que o revisor da Meta lê**. O código real de cada envio vai como parâmetro do botão, então a mesma mensagem aprovada serve para campanhas com códigos diferentes.

## Na criação e no envio

| Na criação                                | No envio                          |
| ----------------------------------------- | --------------------------------- |
| O texto da mensagem e o código de exemplo | O valor de cada variável          |
| Os botões e para onde eles levam          | O código do cupom daquele disparo |

## Campos deste modelo

| Campo             | Para que serve                     | Regra                                                                                                           |
| ----------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `category`        | Fixa                               | Sempre `MARKETING`                                                                                              |
| `HEADER`          | Opcional                           | Texto até 60 caracteres, no máximo uma variável                                                                 |
| `BODY`            | O texto que apresenta o cupom      | Até 1024 caracteres                                                                                             |
| `FOOTER`          | Opcional                           | Até 60 caracteres                                                                                               |
| Botão `COPY_CODE` | Copia o código                     | O `example` é o código: letras, números, hífen e sublinhado, até 20 caracteres. Espaço ou símbolo a Meta recusa |
| Outros botões     | Link, telefone e respostas rápidas | As respostas rápidas ficam **agrupadas**, todas antes ou todas depois dos demais                                |

## 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_coupon_code",
  "category": "MARKETING",
  "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": "COPY_CODE",
          "example": "PROMO10"
        },
        {
          "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": "QUICK_REPLY",
          "text": "Acompanhar pedido"
        }
      ]
    }
  ]
}
```

### 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_coupon_code",
          "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": []
            }
          ]
        }
      }
    ]
  }
}
```

## O que a Meta recusa aqui

* Código com espaço, acento ou símbolo, ou acima de 20 caracteres.
* Mais de um botão de copiar código.
* Resposta rápida no meio dos outros botões: elas precisam ficar juntas.
* Variável no código do cupom, que não é aceita.
