> For the complete documentation index, see [llms.txt](https://docs.caf.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.caf.io/caf-api/caf-api-pt-br/connect/webhook/request.md).

# Requisição

## O que o webhook me envia?

O webhook envia um evento via `HTTP` `POST` requisição com `Content-Type: application/json` com os seguintes parâmetros:

* Um `X-Caf-Signature` cabeçalho usado para verificar se a requisição veio da Certta

{% content-ref url="/pages/c3bfb0401819a393b215c920baeae12483dc3883" %}
[Assinatura](/caf-api/caf-api-pt-br/connect/webhook/signature.md)
{% endcontent-ref %}

* O corpo da requisição é um `JSON` que segue o padrão CloudEvents

{% content-ref url="/pages/d64bfb5898a706ea053a293d72ce5f0317e45ef7" %}
[Eventos](/caf-api/caf-api-pt-br/connect/webhook/events.md)
{% endcontent-ref %}

## Estrutura da requisição

Cada requisição de webhook inclui:

### Cabeçalhos

| Cabeçalho         | Descrição                                               |
| ----------------- | ------------------------------------------------------- |
| `Content-Type`    | Sempre `application/json`                               |
| `User-Agent`      | Identifica-se como `Caf-Webhook/Connect`                |
| `X-Caf-Signature` | Contém a assinatura HMAC SHA-256 do corpo da requisição |

### Corpo da requisição

O corpo da requisição segue o formato CloudEvents, embora os cabeçalhos HTTP específicos do CloudEvents não estejam incluídos:

```json
{
  "specversion": "1.0",
  "type": "COMMUNICATIONCREATEDEVENT",
  "source": "COMMUNICATION",
  "id": "01JZNK5ZQBNF623MB5KE64GNQB",
  "time": "2025-07-08T18:01:19.622Z",
  "datacontenttype": "application/json",
  "data": {
    "tenantId": "016e8f79-2399-4d35-90f9-c6f91b73189d",
    "channel": "sms",
    "externalId": "external-id",
    "notificationId": "01JZNK51YCZXT55TKF8M366QHJ",
    "system": "onboarding",
    "occurredOn": "2025-07-08T18:00:46.797Z"
  }
}
```

## Exemplos

### Curl

Exemplo de comando curl assumindo `SECRET: "dummysecret"`:

```bash
curl --location 'http://localhost:8080/webhook' \
--header 'X-Caf-Signature: 6f9ed23a7b505a3b6907c5f6eb2ad1b056fbf35a643d365a9a072ed7aabca153' \
--header 'Content-Type: application/json' \
--data '{
  "specversion": "1.0",
  "type": "COMMUNICATIONCREATEDEVENT",
  "source": "COMMUNICATION",
  "id": "01JZNK5ZQBNF623MB5KE64GNQB",
  "time": "2025-07-08T18:01:19.622Z",
  "datacontenttype": "application/json",
  "data": {
    "tenantId": "016e8f79-2399-4d35-90f9-c6f91b73189d",
    "channel": "sms",
    "externalId": "external-id",
    "notificationId": "01JZNK51YCZXT55TKF8M366QHJ",
    "system": "onboarding",
    "occurredOn": "2025-07-08T18:00:46.797Z"
  }
}'
```

{% hint style="warning" %}
É importante validar a assinatura do payload assim que ele chegar (como um array de bytes), sem qualquer análise das informações. Isso garante a integridade da verificação.
{% endhint %}

## O que devo responder ao webhook?

{% hint style="info" %}
Nosso webhook considera respostas com um `2xx` código (de preferência `202 ACCEPTED`) em até 2 segundos como indicação de que a integração recebeu o evento com sucesso e, portanto, nenhuma outra chamada será feita para esse evento. O corpo da resposta é ignorado pelo sistema, exceto para fins de auditoria interna em caso de falhas de entrega.
{% endhint %}

## Casos de erro

A requisição de webhook tem o objetivo de integrar o evento com sucesso e nada além disso. Com esse objetivo em mente, as respostas de erro devem ser usadas apenas para indicar falha na integração do evento (por evento integrado, entende-se que o evento foi recebido com sucesso pelo servidor de webhook).

O mecanismo de entrega do webhook aceita e reconhece erros dentro da série de erros HTTP 5xx, que podem indicar erros no recebimento ou processamento da requisição pelo servidor. As retentativas de entrega ocorrerão apenas para essa classe de erros.

As respostas de erro podem seguir o payload especificado abaixo para detalhar e deixar claro o motivo do erro em nossa auditoria interna. Quaisquer outros campos e/ou formatos serão ignorados.

```json
{
  "error": "mensagem de erro"
}
```

{% hint style="danger" %}
Se todas as tentativas de entrega falharem, o webhook descarta o evento, que não será mais entregue via webhook!

O número máximo de tentativas, o intervalo entre cada tentativa de entrega e o tempo limite ficam a critério da Certta. Atualmente, consideramos requisições que demoram mais de 2 segundos para responder como timeout e tentamos reenviar os eventos por até 15 minutos.
{% endhint %}

## Tratamento de requisições de webhook

### Idempotência

{% hint style="info" %}
As requisições de webhook podem ser entregues mais de uma vez em casos raros. Para lidar com isso, implemente idempotência por meio de:

1. Usando o `id` campo no payload do evento para detectar duplicatas
2. Armazenando IDs de eventos processados para evitar processar o mesmo evento duas vezes
3. Tornando sua lógica de tratamento de eventos idempotente (segura para executar várias vezes)
   {% endhint %}

### Exemplo de manipulador de webhook

Aqui está um exemplo simples de manipulador de webhook em Node.js Express:

```javascript
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

const app = express();
app.use(bodyParser.json());

app.post('/webhook', (req, res) => {
  const sigHeader = req.headers['x-caf-signature'];
  const payload = req.body;
  
  // Verifique a assinatura
  if (!verifySignature(payload, sigHeader, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Assinatura inválida');
  }
  
  // Processe o CloudEvent com base em seu tipo
  const eventType = payload.type;
  const eventSource = payload.source;
  const eventId = payload.id;
  
  console.log(`Processando CloudEvent: ${eventId} da origem ${eventSource} do tipo ${eventType}`);
  
  switch (eventType) {
    case 'COMMUNICATIONCREATEDEVENT':
      handleCommunicationCreated(payload.data);
      break;
    case 'TRANSACTIONPROCESSSTARTEDEVENT':
      handleTransactionProcessStarted(payload.data);
      break;
    case 'TRANSACTIONDOCUMENTSCOPYREQUESTEDEVENT':
      handleTransactionDocumentsCopyRequested(payload.data);
      break;
    case 'TRANSACTIONSTATUSUPDATEDEVENT':
      handleTransactionUpdated(payload.data);
      break;
    case 'PROFILEUPDATEDEVENT':
      handleProfileUpdated(payload.data);
      break;
    // Trate outros tipos de evento...
    default:
      console.log(`Tipo de evento não tratado: ${eventType}`);
  }
  
  // Responda com sucesso
  res.status(200).send('Evento recebido');
});

// Inicie o servidor
app.listen(3000, () => {
  console.log('Servidor de webhook ouvindo na porta 3000');
});
```

## Solução de problemas

### Problemas comuns

{% hint style="danger" %}
**401 Unauthorized**

* Seu endpoint retornou um código 401.
* Verifique se você está validando as assinaturas corretamente.

**Tempo limite**

* Seu endpoint demorou demais para responder.
* Otimize seu código para responder em menos de 2 segundos.

**Conexão recusada**

* A Certta não conseguiu se conectar ao seu endpoint.
* Verifique se seu servidor está em execução e acessível.

**Rejeição de assinatura**

* Falha na validação da assinatura do webhook.
* Verifique se você está usando o segredo correto e validando os bytes brutos do corpo.
  {% endhint %}

### Logs e depuração

Você pode usar os logs de webhook no Trust, a plataforma que gerencia configurações, para visualizar o status de entrega dos eventos recentes e quaisquer mensagens de erro. Os logs mantêm um histórico de:

* Tentativas de entrega bem-sucedidas e com falha
* Códigos de resposta HTTP recebidos
* Carimbos de data e hora das entregas
* Erros específicos encontrados durante as entregas


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.caf.io/caf-api/caf-api-pt-br/connect/webhook/request.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
