> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Link de recibo de pagamento via botão de URL

> Configure um sistema externo de pagamento ou reserva para enviar links de recibo do WhatsApp através do Switchbord.

Este guia mostra o padrão de recibo Opção A: o corpo do template de WhatsApp contém os detalhes do recibo e um botão de URL dinâmico abre a página de recibo hospedada.

Use esse padrão quando seu sistema externo já hospeda páginas de recibo e você quer uma mensagem Utility pequena e esperada no WhatsApp.

## Antes de começar

Você precisa de:

* Um canal de WhatsApp conectado no Switchbord.
* Uma chave de API do Switchbord para o workspace, caso chame `POST /api/v1/template-sends` diretamente.
* Um template Utility aprovado com um botão de URL, por exemplo `payment_receipt_link`.
* Uma página de recibo pública em HTTPS, como `https://pay.example.com/receipts/R-1001`.

<Warning>
  Não use este fluxo para mensagens promocionais. Templates Utility devem estar vinculados a eventos esperados pelo cliente, como pagamento, parcela, reserva, reembolso ou disponibilidade de documento.
</Warning>

## Estrutura do template

Um template típico de link de recibo contém variáveis no corpo e um botão de URL:

```text theme={null}
Ciao {{1}}, abbiamo ricevuto il pagamento {{2}} per la ricevuta {{3}}.
```

```text theme={null}
Button URL: https://pay.example.com/receipts/{{1}}
```

A variável do botão é o sufixo dinâmico. Se a URL base for `https://pay.example.com/receipts/`, envie apenas `R-1001` como a variável.

## Envio direto via API

```bash theme={null}
curl -X POST https://api.switchbord.ai/api/v1/template-sends \
  -H "Authorization: Bearer $SWITCHBORD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: payment-receipt-R-1001" \
  -d '{
    "templateName": "payment_receipt_link",
    "templateLocale": "it",
    "recipient": {
      "phoneE164": "+393331234567",
      "externalId": "traveller-123",
      "name": "Giulia Rossi"
    },
    "variables": {
      "body": {
        "1": "Giulia",
        "2": "€ 240,00",
        "3": "R-1001"
      },
      "buttons": {
        "url": [
          { "index": 0, "suffix": "R-1001" }
        ]
      }
    },
    "metadata": {
      "source": "booking-system",
      "eventType": "payment.receipt.created"
    }
  }'
```

Resposta esperada:

```json theme={null}
{
  "data": {
    "contactId": "contact_123",
    "conversationId": "conversation_123",
    "messageId": "message_123",
    "clientMessageId": "payment-receipt-R-1001",
    "status": "queued",
    "reused": false,
    "idempotent": true
  }
}
```

## Construtor de webhook de entrada

Se o sistema externo puder chamar um webhook, mas não puder armazenar uma chave de API do Switchbord, configure um webhook de entrada do workspace em vez disso:

1. Abra **Settings → Integrations → Webhooks & API**.
2. Crie um novo webhook de entrada.
3. Selecione o template Utility aprovado `payment_receipt_link`.
4. Mapeie os campos obrigatórios:

| Campo do template        | Modo de origem     | Exemplo de origem     |
| ------------------------ | ------------------ | --------------------- |
| Telefone do destinatário | Caminho no payload | `customer.phone`      |
| ID externo               | Caminho no payload | `receipt.id`          |
| Corpo `{{1}}`            | Caminho no payload | `customer.first_name` |
| Corpo `{{2}}`            | Caminho no payload | `receipt.amount`      |
| Corpo `{{3}}`            | Caminho no payload | `receipt.number`      |
| Variável do botão de URL | Caminho no payload | `receipt.number`      |

1. Cole um payload de exemplo e execute o preview de dry-run.
2. Crie o webhook. O endpoint permanece desabilitado/dry-run até que um futuro fluxo de promoção verificado por run-log habilite a execução real.

Payload de exemplo:

```json theme={null}
{
  "customer": {
    "phone": "+393331234567",
    "first_name": "Giulia"
  },
  "receipt": {
    "id": "payment-receipt-R-1001",
    "number": "R-1001",
    "amount": "€ 240,00",
    "link": "https://pay.example.com/receipts/R-1001"
  }
}
```

## Assinando requisições de webhook de entrada

As requisições de webhook usam assinaturas HMAC com timestamp. O segredo é exibido apenas uma vez, quando o webhook é criado.

```ts theme={null}
import { createHash, createHmac } from "node:crypto";

const rawBody = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000);
const secretHash = createHash("sha256").update(webhookSecret).digest("hex");
const digest = createHmac("sha256", secretHash)
  .update(`${timestamp}.${rawBody}`)
  .digest("hex");

await fetch(webhookUrl, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Switchbord-Signature": `t=${timestamp},v1=${digest}`,
    "Idempotency-Key": payload.receipt.id
  },
  body: rawBody
});
```

Nunca envie o segredo bruto do webhook como um cabeçalho, parâmetro de query ou campo JSON.

## Checklist de lançamento

* Execute o preview de dry-run no navegador e verifique a requisição mapeada.
* Não espere ainda run logs de endpoint assinado para configs criadas em Settings; eles permanecem desabilitados até que a promoção real seja lançada.
* Confirme que o template é Utility aprovado no mesmo workspace.
* Use chaves de idempotência estáveis a partir do id do evento de origem.
* Mantenha as URLs de recibo somente em HTTPS e evite embutir tokens privados de longa duração.

## Solução de problemas

| Sintoma                             | Verificação                                                                                                                       |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `404 webhook_not_found_or_disabled` | Configs criadas em Settings estão desabilitadas hoje; use o preview no navegador ou a API direta até que a promoção seja lançada. |
| Futuro `401 missing_timestamp`      | `X-Switchbord-Signature` deve incluir `t=<segundos unix>`.                                                                        |
| Futuro `401 expired_timestamp`      | O relógio do remetente difere em mais de cinco minutos.                                                                           |
| Futuro `401 invalid_signature`      | Assine o corpo bruto exato da requisição, não uma cópia reserializada.                                                            |
| `400 template_not_approved_utility` | Selecione um template Utility aprovado no mesmo workspace.                                                                        |
| `400 missing_button_url_variable`   | Mapeie o sufixo do botão de URL e garanta que ele resolva para uma string não vazia.                                              |
| Envios duplicados                   | Use uma `Idempotency-Key` estável para cada evento de recibo.                                                                     |
