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

# Gerenciar webhooks

> Cadastre um endpoint, dispare um teste, investigue entregas com falha e segure o volume com supressão.

Este guia usa a API do vendedor para cuidar dos webhooks da empresa: cadastrar, testar, investigar falhas e controlar volume. Para o formato de cada evento, veja a seção **Eventos**.

## 1. Cadastre o endpoint

Pelo painel, o cadastro fica em **Integrações › Webhooks**. Pela API, use `POST /v1/webhooks`.

![Formulário de novo webhook com URL e eventos](https://mintcdn.com/ephraio-2770e271/j9pHfIg8y1UXAJx2/images/guias/webhook-novo.png?fit=max\&auto=format\&n=j9pHfIg8y1UXAJx2\&q=85\&s=32c0ad24f08abe3469bb524d1dc33e31)

Integrações › Webhooks › Novo webhook

> [!WARNING]
>
> A Ephra **testa a URL no momento do cadastro**, com um `POST` de verdade. Um endereço que não responde `2xx` recusa o cadastro:
>
> ```json theme={"system"}
> {
>   "success": false,
>   "message": "Não foi possivel testar a url do webhook"
> }
> ```
>
> Suba o endpoint antes de cadastrá-lo, e faça-o responder `200` a qualquer corpo.

```bash cURL theme={"system"}
curl -s -X POST https://api.ephra.io/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://erp.minhaloja.com.br/integracoes/ephra",
    "events": ["transaction_paid", "transaction_refunded"],
    "name": "ERP da loja",
    "isActive": true,
    "productIds": ["cmty05wl0000apyg1bx1566ej"]
  }'
```

```javascript Node theme={"system"}
const res = await fetch("https://api.ephra.io/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://erp.minhaloja.com.br/integracoes/ephra",
    "events": [
      "transaction_paid",
      "transaction_refunded"
    ],
    "name": "ERP da loja",
    "isActive": true,
    "productIds": [
      "cmty05wl0000apyg1bx1566ej"
    ]
  }),
});
const json = await res.json();
```

```python Python theme={"system"}
import requests

res = requests.post(
    "https://api.ephra.io/v1/webhooks",
    headers={
        "Authorization": f"Bearer {token}",
    },
    json={
        "url": "https://erp.minhaloja.com.br/integracoes/ephra",
        "events": [
            "transaction_paid",
            "transaction_refunded"
        ],
        "name": "ERP da loja",
        "isActive": True,
        "productIds": [
            "cmty05wl0000apyg1bx1566ej"
        ]
    },
)
data = res.json()
```

```json theme={"system"}
{
  "success": true,
  "data": {
    "id": 20,
    "name": "ERP da loja",
    "url": "https://erp.minhaloja.com.br/integracoes/ephra",
    "events": [
      "transaction_paid",
      "transaction_refunded"
    ],
    "isActive": true,
    "productIds": [
      "cmty05wl0000apyg1bx1566ej"
    ],
    "offerIds": [],
    "signatureSecretMasked": "••••••••146b",
    "mtlsCapable": false,
    "createdAt": "2026-09-12T06:28:41.855Z",
    "updatedAt": "2026-09-12T06:28:41.855Z"
  }
}
```

`productIds` vazio significa "todos os produtos". Preenchido, o webhook só dispara para os produtos listados: é assim que se separa um ERP que só cuida de uma linha de produtos.

> [!NOTE]
>
> O segredo de assinatura volta **mascarado** (`signatureSecretMasked`). Ele ainda não assina as entregas: leia [Verificação e segurança](https://docs.ephra.io/webhooks/seguranca.md) antes de construir qualquer validação em cima dele.

## 2. Dispare um teste

```bash cURL theme={"system"}
curl -s -X POST https://api.ephra.io/v1/webhooks/20/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"events": ["transaction_paid"]}'
```

```javascript Node theme={"system"}
const res = await fetch("https://api.ephra.io/v1/webhooks/20/test", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "events": [
      "transaction_paid"
    ]
  }),
});
const json = await res.json();
```

```python Python theme={"system"}
import requests

res = requests.post(
    "https://api.ephra.io/v1/webhooks/20/test",
    headers={
        "Authorization": f"Bearer {token}",
    },
    json={
        "events": [
            "transaction_paid"
        ]
    },
)
data = res.json()
```

```json theme={"system"}
{
  "success": true,
  "data": {
    "webhookId": 20,
    "url": "https://erp.minhaloja.com.br/integracoes/ephra",
    "results": [
      {
        "event": "transaction_paid",
        "success": true,
        "httpStatus": 200,
        "error": null
      }
    ]
  }
}
```

O `httpStatus` é o que **o seu servidor** respondeu. O payload que chega é um evento completo com `scope: "test"` e ids de teste:

```json theme={"system"}
{
  "id": "3ff1d2f6-386a-4627-8d45-570b418ab970",
  "type": "transaction",
  "event": "transaction_paid",
  "scope": "test",
  "transaction": {
    "id": "test_transaction_id",
    "amount": 49900,
    "netAmount": 44910,
    "status": "paid",
    "pix": {
      "endToEndId": "E00000000202506301200TEST00000001",
      "payerInfo": {
        "name": "Cliente Teste",
        "document": "00000000000"
      }
    },
    "paidAt": "2026-09-12T06:28:41.924Z",
    "refundPeriodDays": 7,
    "refundDeadline": "2026-09-19T23:59:59-03:00"
  },
  "customer": {
    "email": "cliente.teste@example.com",
    "name": "Cliente Teste",
    "phone": "+5511999999999",
    "document": "00000000000",
    "documentType": "CPF",
    "purchaseDate": "2026-09-12T06:28:41.925Z"
  },
  "product": {
    "id": "cmty05wl0000apyg1bx1566ej",
    "name": "Curso de Tráfego Pago 2026",
    "amount": 49900
  },
  "company": {
    "name": "Fixtures Empresa A",
    "document": "11111111000191",
    "documentType": "CNPJ"
  },
  "sale": {
    "type": "one_time",
    "subscriptionInterval": null,
    "renewalType": null,
    "mainOfferId": null,
    "mainOfferName": null,
    "orderBumpIds": [],
    "orderBumpOfferIds": [],
    "orderBumps": []
  }
}
```

> [!TIP]
>
> Filtre por `scope: "test"` no seu endpoint e descarte antes de liberar qualquer pedido. Um teste com `transaction.id: "test_transaction_id"` que libera acesso de verdade é um bug caro.

## 3. Investigue o que falhou

```bash cURL theme={"system"}
curl -s "https://api.ephra.io/v1/webhook-deliveries?page=1&pageSize=3" \
  -H "Authorization: Bearer $TOKEN"
```

```javascript Node theme={"system"}
const res = await fetch("https://api.ephra.io/v1/webhook-deliveries?page=1&pageSize=3", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});
const json = await res.json();
```

```python Python theme={"system"}
import requests

res = requests.get(
    "https://api.ephra.io/v1/webhook-deliveries?page=1&pageSize=3",
    headers={
        "Authorization": f"Bearer {token}",
    },
)
data = res.json()
```

```json theme={"system"}
{
  "success": true,
  "data": [
    {
      "id": "3453856f-596f-493e-b2b0-86f0542f02d8",
      "transactionId": "cmtxyt3710000eag1smxe72ee",
      "type": "transaction",
      "event": "transaction_refunded",
      "eventType": "user",
      "status": "error",
      "url": "https://erp.minhaloja.com.br/integracoes/ephra",
      "retries": 1,
      "createdAt": "2026-09-12T05:50:09.057Z",
      "updatedAt": "2026-09-12T05:50:09.102Z"
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 3,
    "total": 1,
    "totalPages": 1
  }
}
```

`status: "error"` com `retries: 1` significa que a primeira tentativa falhou e a entrega está na fila de retentativa. `GET /v1/webhook-deliveries/{deliveryId}` traz o corpo enviado e a resposta recebida: é onde você descobre que o seu servidor devolveu `500` numa data inválida.

Para forçar um reenvio depois de corrigir o bug do seu lado:

```bash cURL theme={"system"}
curl -s -X POST https://api.ephra.io/v1/webhook-deliveries/3453856f-596f-493e-b2b0-86f0542f02d8/retry \
  -H "Authorization: Bearer $TOKEN"
```

```javascript Node theme={"system"}
const res = await fetch("https://api.ephra.io/v1/webhook-deliveries/3453856f-596f-493e-b2b0-86f0542f02d8/retry", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
  },
});
const json = await res.json();
```

```python Python theme={"system"}
import requests

res = requests.post(
    "https://api.ephra.io/v1/webhook-deliveries/3453856f-596f-493e-b2b0-86f0542f02d8/retry",
    headers={
        "Authorization": f"Bearer {token}",
    },
)
data = res.json()
```

## 4. Segure o volume

Em pico de lançamento, `transaction_created` pode chegar às centenas por minuto. A supressão descarta parte das entregas de um evento, numa janela fixa.

```bash cURL theme={"system"}
curl -s -X POST https://api.ephra.io/v1/webhook-suppressions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "transaction_created",
    "interval": 10,
    "suppressCount": 9,
    "isActive": true
  }'
```

```javascript Node theme={"system"}
const res = await fetch("https://api.ephra.io/v1/webhook-suppressions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "event": "transaction_created",
    "interval": 10,
    "suppressCount": 9,
    "isActive": true
  }),
});
const json = await res.json();
```

```python Python theme={"system"}
import requests

res = requests.post(
    "https://api.ephra.io/v1/webhook-suppressions",
    headers={
        "Authorization": f"Bearer {token}",
    },
    json={
        "event": "transaction_created",
        "interval": 10,
        "suppressCount": 9,
        "isActive": True
    },
)
data = res.json()
```

```json theme={"system"}
{
  "success": true,
  "data": {
    "id": 1,
    "event": "transaction_created",
    "interval": 10,
    "suppressCount": 9,
    "counter": 0,
    "isActive": true,
    "createdAt": "2026-09-12T05:36:00.708Z",
    "updatedAt": "2026-09-12T05:36:00.708Z"
  }
}
```

Leia assim: a cada 10 disparos, 9 são descartados e 1 chega. `suppressCount` precisa ser menor que `interval`, e `interval` no mínimo 2. Omitir o `event` reprova:

```json theme={"system"}
{
  "success": false,
  "message": "event: Required"
}
```

> [!WARNING]
>
> Supressão **perde eventos de verdade**: não é amostragem inteligente, é descarte por contagem. Nunca a use em `transaction_paid`, `transaction_refunded` ou `infraction`: são os eventos que liberam acesso e movem dinheiro. Reserve-a para `transaction_created` e outros sinais de volume.

## Todas as operações

> [card] [Webhooks](https://docs.ephra.io/api-reference/plataforma/listar-webhooks.md)
>
> Cadastrar, atualizar, testar e excluir endpoints.

> [card] [Entregas](https://docs.ephra.io/api-reference/plataforma/listar-entregas-de-webhook.md)
>
> Histórico, detalhe de cada tentativa e reenvio.

> [card] [Supressões](https://docs.ephra.io/api-reference/plataforma/listar-supressões-de-webhook.md)
>
> Regras de descarte por evento e janela.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.