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

# Cupón

> Mensaje con un código de descuento que el cliente copia con un 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`

Mensaje con un código de descuento que se copia con un toque.

<TemplatePreview lang="es" header="Llegó tu recompensa" body="{{nombre_del_cliente}}, ganaste un cupón de 10% en tu próxima compra. Toca para copiar el código." coupon="VUELVE10" footer="Válido hasta fin de mes" buttons={['Copiar código', 'Ver detalles', 'Llamar a la tienda', 'Seguir pedido']} caption="Código que se copia, enlace, teléfono y respuesta rápida — las respuestas rápidas agrupadas al final, como exige Meta." />

## Cómo funciona

El botón de copiar código es el corazón del modelo: el cliente toca y el código va al portapapeles, listo para pegar en tu sitio o en tu aplicación.

El código que pones en la creación es la **muestra que lee el revisor de Meta**. El código real de cada envío va como parámetro del botón, así que el mismo mensaje aprobado sirve para campañas con códigos distintos.

## En la creación y en el envío

| En la creación                              | En el envío                        |
| ------------------------------------------- | ---------------------------------- |
| El texto del mensaje y el código de ejemplo | El valor de cada variable          |
| Los botones y adónde llevan                 | El código del cupón de ese disparo |

## Campos de este modelo

| Campo             | Para qué sirve                        | Regla                                                                                                                 |
| ----------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `category`        | Fija                                  | Siempre `MARKETING`                                                                                                   |
| `HEADER`          | Opcional                              | Texto hasta 60 caracteres, como máximo una variable                                                                   |
| `BODY`            | El texto que presenta el cupón        | Hasta 1024 caracteres                                                                                                 |
| `FOOTER`          | Opcional                              | Hasta 60 caracteres                                                                                                   |
| Botón `COPY_CODE` | Copia el código                       | El `example` es el código: letras, números, guion y guion bajo, hasta 20 caracteres. Meta rechaza espacios y símbolos |
| Otros botones     | Enlace, teléfono y respuestas rápidas | Las respuestas rápidas quedan **agrupadas**, todas antes o todas después de los demás                                 |

## JSON completo

Son dos JSON: uno **crea** la plantilla, y Meta la aprueba una vez. El otro **envía** el mensaje, y va en cada disparo, con los valores de ese envío en las variables marcadas con llaves.

### 1. Crear la plantilla

`POST /whatsapp/businesses/{wabaId}/templates` — ver en [Referencia de la API](/es/templates/create-template).

```json theme={null}
{
  "name": "exemplo_coupon_code",
  "category": "MARKETING",
  "language": "es_ES",
  "allow_category_change": true,
  "parameter_format": "NAMED",
  "components": [
    {
      "type": "HEADER",
      "format": "TEXT",
      "text": "Pedido {{numero_del_pedido}}",
      "example": {
        "header_text_named_params": [
          {
            "param_name": "numero_del_pedido",
            "example": "1042"
          }
        ]
      }
    },
    {
      "type": "BODY",
      "text": "Hola {{nombre_del_cliente}}, tu pedido {{numero_del_pedido}} quedó confirmado y sale hoy para entrega.",
      "example": {
        "body_text_named_params": [
          {
            "param_name": "nombre_del_cliente",
            "example": "Marina"
          },
          {
            "param_name": "numero_del_pedido",
            "example": "1042"
          }
        ]
      }
    },
    {
      "type": "FOOTER",
      "text": "Responde BAJA para no recibir más mensajes"
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "COPY_CODE",
          "example": "PROMO10"
        },
        {
          "type": "URL",
          "text": "Ver detalles",
          "url": "https://z-api.io/pedido/{{numero_do_pedido}}",
          "example": [
            "https://z-api.io/pedido/1042"
          ]
        },
        {
          "type": "PHONE_NUMBER",
          "text": "Hablar con la tienda",
          "phone_number": "+5511988881234"
        },
        {
          "type": "QUICK_REPLY",
          "text": "Seguir mi pedido"
        }
      ]
    }
  ]
}
```

### 2. Enviar después de la aprobación

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

```json theme={null}
{
  "recipient": {
    "identifier": "5511999999999"
  },
  "content": {
    "type": "TEMPLATE",
    "attachments": [
      {
        "template": {
          "name": "exemplo_coupon_code",
          "language": {
            "policy": "deterministic",
            "code": "es_ES"
          },
          "components": [
            {
              "type": "header",
              "parameters": [
                {
                  "type": "text",
                  "text": "{{numero_del_pedido}}"
                }
              ]
            },
            {
              "type": "body",
              "parameters": [
                {
                  "type": "text",
                  "text": "{{nombre_del_cliente}}"
                },
                {
                  "type": "text",
                  "text": "{{numero_del_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": []
            }
          ]
        }
      }
    ]
  }
}
```

## Lo que Meta rechaza aquí

* Código con espacio, acento o símbolo, o de más de 20 caracteres.
* Más de un botón de copiar código.
* Respuesta rápida en medio de los otros botones: tienen que quedar juntas.
* Variable en el código del cupón, que no se acepta.
