> For the complete documentation index, see [llms.txt](https://hubla.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hubla.gitbook.io/docs/webhooks/como-enviar-seus-dados.md).

# Como enviar seus dados

Cada regra de webhook tem um **modo de envio**, escolhido no campo "Como enviar seus dados" quando você cria ou edita a regra. São dois:

* **Integração recomendada** — envia um aviso por venda, com todos os produtos dentro dele.
* **Modo de compatibilidade** — o comportamento antigo: numa venda com mais de um produto, envia dois avisos para cada produto. Existe só para ferramentas que ainda esperam o formato anterior.

Esta página explica exatamente o que muda entre os dois, para você decidir com segurança.

{% hint style="success" %}
**Nada muda sozinho.** Se a sua integração já funciona hoje, ela continua funcionando. A troca de modo é uma ação sua, na regra.
{% endhint %}

{% hint style="info" %}
**Como saber qual modo a sua regra usa:** abra a integração de Webhook no painel, edite a regra e olhe o campo "Como enviar seus dados". A opção marcada é o modo atual. Se você não vê esse campo, a sua regra está no modo de compatibilidade.

No código, você não precisa adivinhar: toda requisição chega com o cabeçalho `x-hubla-delivery-mode`, valendo `standard` (integração recomendada) ou `compatibility` (modo de compatibilidade). Veja [Modo de envio na requisição](/docs/webhooks/proteja-seu-endpoint.md#modo-de-envio-na-requisicao).
{% endhint %}

***

## O que muda, em resumo

|                                                                     | Modo de compatibilidade                                                    | Integração recomendada     |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------- |
| Avisos numa venda de 1 produto                                      | 1                                                                          | 1                          |
| Avisos numa venda com order bump de N produtos                      | 2 × N                                                                      | 1                          |
| Conteúdo de `products`                                              | só o produto daquele aviso                                                 | todos os produtos da venda |
| Valor de `products[].id`                                            | id da oferta principal do produto                                          | id do produto              |
| Campos de order bump (`hasOrderBump`, `isOrderBump`, `amountCents`) | ausentes                                                                   | presentes                  |
| `user`                                                              | igual nos dois modos                                                       | igual nos dois modos       |
| `invoice`, `subscriptions`                                          | os da venda real nos avisos da venda; **fabricados** nos avisos do produto | sempre os da venda real    |

São duas mudanças independentes, e elas quebram integrações de formas diferentes. A contagem de avisos faz a sua automação rodar mais de uma vez e a receita ser somada em duplicidade. O `products[].id` faz a busca do produto no seu sistema não encontrar nada — sem erro, sem aviso. Vale ler as duas seções abaixo mesmo que você nunca tenha vendido order bump.

***

## Quantos avisos você recebe

### Venda de um produto

Um aviso, nos dois modos. Aqui não há diferença de quantidade.

### Venda com order bump

É onde os modos divergem. Numa venda com **N** produtos:

* **Integração recomendada** — **1 aviso**, representando a venda inteira.
* **Modo de compatibilidade** — **2 × N avisos**. Numa venda de 3 produtos, são 6 requisições para a sua URL.

Os 2 × N avisos são de dois tipos, e cada um tem um problema próprio.

**N "avisos da venda"** — cada um traz a fatura real, com o **valor total da venda inteira**. Eles diferem entre si apenas pelos blocos `product` e `products`. Numa venda de R$ 341,00 com 3 produtos, chegam 3 avisos dizendo, cada um, que a venda vale R$ 341,00. Somar os três dá R$ 1.023,00.

**N "avisos do produto"** — cada um traz uma fatura **fabricada** para representar um item isolado. Nesses avisos:

* `invoice.id` recebe um sufixo de posição, no formato `<id-da-fatura>-offer-<posição>`. **Esse identificador não existe na Hubla** — não é consultável na API nem aparece no painel.
* `invoice.parentInvoiceId` aponta para a fatura real.
* Todos os valores em centavos são rateados pela participação do item no subtotal.

O rateio é uma divisão simples, sem arredondamento. Quando a divisão não é exata, o valor sai fracionário — em um campo que representa centavos. No exemplo da seção [Veja na prática](#veja-na-pratica), o repasse do vendedor no aviso do segundo produto sai como `3618.9999999999995`.

{% hint style="danger" %}
**O cabeçalho `x-hubla-idempotency` não resolve essa duplicação.** Ele identifica o **aviso**, não a venda: os 6 avisos do exemplo acima chegam com 6 valores diferentes. Deduplicar por ele protege contra o reenvio do mesmo aviso, e é isso que ele deve fazer — mas não junta os avisos de uma mesma venda.

Para colapsar os avisos duplicados de uma venda, combine três campos: `type`, `event.invoice.parentInvoiceId ?? event.invoice.id` e `event.invoice.status`.

A identidade da venda **sozinha** não serve como chave: a mesma venda gera eventos de tipos diferentes ao longo do tempo, e `invoice.status_updated` é enviado em **toda** transição de status. Deduplicar só pela venda descartaria eventos legítimos — o reembolso de uma venda que você já processou como paga, por exemplo.
{% endhint %}

***

## O que vem dentro de cada aviso

Esta parte muda em **todos** os eventos de fatura, inclusive numa venda de um único produto.

{% hint style="danger" %}
**`products[].id` muda de valor.** No modo de compatibilidade ele carrega o id da **oferta principal** do produto. Na integração recomendada ele carrega o **id do produto**.

São valores diferentes. Se o seu sistema busca o produto por `products[0].id`, essa busca deixa de encontrar — normalmente sem lançar erro, o que faz a falha passar despercebida. É o primeiro campo a revisar antes de trocar o modo.

O campo `product` (singular) **não mudou**: ele continua trazendo o id da oferta principal nos dois modos. Só o array `products` foi alterado.
{% endhint %}

### De-para dos campos

| Campo                             | Modo de compatibilidade                                                                                                                            | Integração recomendada                                        |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `products`                        | só o produto daquele aviso                                                                                                                         | todos os produtos da venda, agrupados por produto             |
| `products[].id`                   | id da oferta principal do produto                                                                                                                  | id do produto                                                 |
| `products[].name`                 | nome do produto                                                                                                                                    | descrição do item na fatura                                   |
| `products[].offers[].amountCents` | ausente                                                                                                                                            | valor da oferta em centavos                                   |
| `products[].offers[].isOrderBump` | ausente                                                                                                                                            | `true` em toda oferta que não é a principal                   |
| `hasOrderBump`                    | ausente                                                                                                                                            | `true` quando a venda tem alguma oferta diferente da primeira |
| `product` (singular)              | id da oferta principal                                                                                                                             | **igual** — não mudou                                         |
| `user`                            | —                                                                                                                                                  | **igual**                                                     |
| `invoice`                         | a fatura real nos avisos da venda; uma fatura **fabricada** nos avisos do produto (`id` sintético, `parentInvoiceId` preenchido, valores rateados) | sempre a fatura real                                          |
| `subscriptions`                   | a assinatura da venda nos avisos da venda; a do item isolado nos avisos do produto                                                                 | a assinatura da venda                                         |

### Detalhes que valem saber

**Ofertas do mesmo produto são agrupadas.** Se a venda tem duas ofertas do mesmo produto, elas aparecem como **uma** entrada em `products`, com duas entradas em `offers`. O agrupamento é pelo produto, não pela oferta.

**`products[].name` vem da fatura, não do cadastro.** Na integração recomendada esse campo traz a descrição do item como ela foi registrada na fatura, que pode incluir o plano — por exemplo `Integrações com Webhook 2.0 - Anual`, enquanto `product.name` traz `Integrações com Webhook 2.0`. Se você exibe esse nome para o cliente, confira qual dos dois você quer.

**No modo de compatibilidade, `products[].id` e `offers[].id` divergem numa venda de order bump.** `products[].id` é o id da **oferta principal do produto**; `offers[].id` é o id da oferta que o cliente realmente comprou. No exemplo da seção [Veja na prática](#veja-na-pratica), o aviso do segundo produto traz `products[0].id` = `tpl7Kq2wRt4Ns9XbY1Lm` (a oferta principal daquele produto) e `offers[0].id` = `bmpTPL1aX9Kq2wRt4Ns7` (a oferta de order bump comprada). Nenhum dos dois é o id do produto.

**`isOrderBump` é posicional.** A primeira oferta da fatura é a principal; todas as outras são marcadas como order bump. Não é uma configuração da oferta no cadastro — a mesma oferta pode chegar com `isOrderBump: false` numa venda e `true` em outra.

**`childInvoiceIds` continua preenchido** numa venda com order bump, também na integração recomendada. Mas nesse modo aqueles identificadores **não correspondem a avisos que você vai receber** — você recebe um único aviso.

{% hint style="warning" %}
**O `version` da raiz do aviso é `"2.0.0"` nos dois modos.** Não confunda com `event.invoice.version`, que é o número de versão da fatura (`6` nos exemplos desta página) e também não identifica o modo. Não existe nada no corpo do aviso que diga qual modo o gerou. Se o seu código precisa tratar os dois formatos durante uma transição, use a presença de `hasOrderBump` ou o formato de `products[].id` — não o `version`.
{% endhint %}

***

## Veja na prática

A mesma venda nos dois modos: pagamento aprovado (`invoice.payment_succeeded`) de **R$ 341,00 em pix**, com um produto principal e dois order bumps.

| Item       | Produto                     | Valor     |
| ---------- | --------------------------- | --------- |
| Principal  | Integrações com Webhook 2.0 | R$ 197,00 |
| Order bump | Templates de Automação      | R$ 47,00  |
| Order bump | Mentoria em Grupo           | R$ 97,00  |

{% tabs %}
{% tab title="Integração recomendada" %}
**1 requisição.** Um aviso com os três produtos dentro.

```json
{
  "type": "invoice.payment_succeeded",
  "event": {
    "product": {
      "id": "inAVzweR0QYw5y03K5mq",
      "name": "Integrações com Webhook 2.0"
    },
    "products": [
      {
        "id": "9f1c4e2a-7b3d-4c8e-a1f5-2d6b8e0c3a71",
        "name": "Integrações com Webhook 2.0 - Anual",
        "offers": [
          {
            "id": "inAVzweR0QYw5y03K5mq",
            "name": "Principal",
            "cohorts": [{ "id": "3a96c85e-cf82-4236-b242-7431f405c521" }],
            "amountCents": 19700,
            "isOrderBump": false
          }
        ]
      },
      {
        "id": "4a7d9c1e-2f5b-48a3-9c6d-7e1b3f8a5d02",
        "name": "Templates de Automação",
        "offers": [
          {
            "id": "bmpTPL1aX9Kq2wRt4Ns7",
            "name": "Oferta única",
            "cohorts": [{ "id": "7d2f1a95-4b6c-42e8-9f3a-1c8e5b0d7a24" }],
            "amountCents": 4700,
            "isOrderBump": true
          }
        ]
      },
      {
        "id": "6b2e8f4a-9d1c-4e7b-8a3f-5c9d2e6b1a48",
        "name": "Mentoria em Grupo",
        "offers": [
          {
            "id": "bmpMNT2bY8Lr3xSu5Ot9",
            "name": "Oferta única",
            "cohorts": [{ "id": "b41e7c20-9d58-4f3a-8b2e-6a0c4d1f9e37" }],
            "amountCents": 9700,
            "isOrderBump": true
          }
        ]
      }
    ],
    "hasOrderBump": true,
    "invoice": {
      "id": "7614b1bb-1d1a-43ba-890c-50d74216eb56",
      "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "parentInvoiceId": null,
      "childInvoiceIds": [
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-1",
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-2",
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-3"
      ],
      "subscriptionId": "d7ab5027-5bca-476c-ac29-68f1359ee6ca",
      "payerId": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "sellerId": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
      "payer": {
        "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
        "firstName": "John",
        "lastName": "Doe",
        "document": "12345678000190",
        "email": "johndoe.payer@example.com",
        "phone": "+5511999999999"
      },
      "installments": 1,
      "paymentMethod": "pix",
      "currency": "BRL",
      "type": "sell",
      "status": "paid",
      "statusAt": [
        { "status": "unpaid", "when": "2024-03-28T20:35:22.671Z" },
        { "status": "paid", "when": "2024-03-28T20:35:33.512Z" }
      ],
      "amount": {
        "subtotalCents": 34100,
        "subtotal": 341,
        "discountCents": 0,
        "discount": 0,
        "prorataCents": 0,
        "prorata": 0,
        "installmentFeeCents": 0,
        "installmentFee": 0,
        "totalCents": 34100,
        "total": 341
      },
      "receivers": [
        {
          "id": "platform-identity",
          "name": "Hubla Tecnologia LTDA",
          "email": "suporte@hub.la",
          "phone": "+5531999715693",
          "role": "platform",
          "paysForFees": false,
          "totalCents": 7843,
          "currency": "BRL"
        },
        {
          "id": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
          "name": "Victor Lott",
          "email": "lott@hub.la",
          "phone": "+5533988891169",
          "role": "seller",
          "paysForFees": true,
          "totalCents": 26257,
          "currency": "BRL"
        }
      ],
      "paymentSession": {
        "ip": "220.172.165.140",
        "url": "https://pay.hub.la/inAVzweR0QYw5y03K5mq",
        "utm": {
          "source": "email",
          "medium": "active-campaign",
          "campaign": "[LEAD][QUENTE][24][MAR]"
        }
      },
      "saleDate": "2024-03-28T20:35:22.671Z",
      "dueDate": "2024-03-28T20:35:16.350Z",
      "createdAt": "2024-03-28T20:35:22.671Z",
      "modifiedAt": "2024-03-28T20:35:33.512Z",
      "version": 6
    },
    "subscriptions": [
      {
        "id": "d7ab5027-5bca-476c-ac29-68f1359ee6ca",
        "type": "recurring",
        "billingCycleMonths": 12,
        "quantity": 1
      }
    ],
    "user": {
      "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "firstName": "John",
      "lastName": "Doe",
      "document": "12345678900",
      "email": "johndoe.user@example.com",
      "phone": "+5511999999999"
    }
  },
  "version": "2.0.0"
}
```

{% endtab %}

{% tab title="Modo de compatibilidade" %}
**6 requisições:** 3 avisos da venda e 3 avisos de produto. Abaixo, um de cada tipo.

**Aviso da venda (1 de 3)** — `products` traz só o produto principal, e `amount` traz o total da venda inteira. Os outros dois avisos da venda são idênticos a este, mudando apenas `product` e `products` para o segundo e o terceiro produto.

```json
{
  "type": "invoice.payment_succeeded",
  "event": {
    "product": {
      "id": "inAVzweR0QYw5y03K5mq",
      "name": "Integrações com Webhook 2.0"
    },
    "products": [
      {
        "id": "inAVzweR0QYw5y03K5mq",
        "name": "Integrações com Webhook 2.0",
        "offers": [
          {
            "id": "inAVzweR0QYw5y03K5mq",
            "name": "Principal",
            "cohorts": [{ "id": "3a96c85e-cf82-4236-b242-7431f405c521" }]
          }
        ]
      }
    ],
    "invoice": {
      "id": "7614b1bb-1d1a-43ba-890c-50d74216eb56",
      "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "parentInvoiceId": null,
      "childInvoiceIds": [
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-1",
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-2",
        "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-3"
      ],
      "subscriptionId": "d7ab5027-5bca-476c-ac29-68f1359ee6ca",
      "payerId": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "sellerId": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
      "payer": {
        "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
        "firstName": "John",
        "lastName": "Doe",
        "document": "12345678000190",
        "email": "johndoe.payer@example.com",
        "phone": "+5511999999999"
      },
      "installments": 1,
      "paymentMethod": "pix",
      "currency": "BRL",
      "type": "sell",
      "status": "paid",
      "statusAt": [
        { "status": "unpaid", "when": "2024-03-28T20:35:22.671Z" },
        { "status": "paid", "when": "2024-03-28T20:35:33.512Z" }
      ],
      "amount": {
        "subtotalCents": 34100,
        "subtotal": 341,
        "discountCents": 0,
        "discount": 0,
        "prorataCents": 0,
        "prorata": 0,
        "installmentFeeCents": 0,
        "installmentFee": 0,
        "totalCents": 34100,
        "total": 341
      },
      "receivers": [
        {
          "id": "platform-identity",
          "name": "Hubla Tecnologia LTDA",
          "email": "suporte@hub.la",
          "phone": "+5531999715693",
          "role": "platform",
          "paysForFees": false,
          "totalCents": 7843,
          "currency": "BRL"
        },
        {
          "id": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
          "name": "Victor Lott",
          "email": "lott@hub.la",
          "phone": "+5533988891169",
          "role": "seller",
          "paysForFees": true,
          "totalCents": 26257,
          "currency": "BRL"
        }
      ],
      "paymentSession": {
        "ip": "220.172.165.140",
        "url": "https://pay.hub.la/inAVzweR0QYw5y03K5mq",
        "utm": {
          "source": "email",
          "medium": "active-campaign",
          "campaign": "[LEAD][QUENTE][24][MAR]"
        }
      },
      "saleDate": "2024-03-28T20:35:22.671Z",
      "dueDate": "2024-03-28T20:35:16.350Z",
      "createdAt": "2024-03-28T20:35:22.671Z",
      "modifiedAt": "2024-03-28T20:35:33.512Z",
      "version": 6
    },
    "subscriptions": [
      {
        "id": "d7ab5027-5bca-476c-ac29-68f1359ee6ca",
        "type": "recurring",
        "billingCycleMonths": 12,
        "quantity": 1
      }
    ],
    "user": {
      "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "firstName": "John",
      "lastName": "Doe",
      "document": "12345678900",
      "email": "johndoe.user@example.com",
      "phone": "+5511999999999"
    }
  },
  "version": "2.0.0"
}
```

**Aviso do produto (2 de 3)** — referente ao primeiro order bump. Repare em `invoice.id` com o sufixo `-offer-2`, em `parentInvoiceId` apontando para a fatura real, e no repasse do vendedor rateado sem arredondamento.

```json
{
  "type": "invoice.payment_succeeded",
  "event": {
    "product": {
      "id": "tpl7Kq2wRt4Ns9XbY1Lm",
      "name": "Templates de Automação"
    },
    "products": [
      {
        "id": "tpl7Kq2wRt4Ns9XbY1Lm",
        "name": "Templates de Automação",
        "offers": [
          {
            "id": "bmpTPL1aX9Kq2wRt4Ns7",
            "name": "Oferta única",
            "cohorts": [{ "id": "7d2f1a95-4b6c-42e8-9f3a-1c8e5b0d7a24" }]
          }
        ]
      }
    ],
    "invoice": {
      "id": "7614b1bb-1d1a-43ba-890c-50d74216eb56-offer-2",
      "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "parentInvoiceId": "7614b1bb-1d1a-43ba-890c-50d74216eb56",
      "childInvoiceIds": [],
      "subscriptionId": "e8bc6138-6cdb-487d-bd3a-79f246aff7db",
      "payerId": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "sellerId": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
      "payer": {
        "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
        "firstName": "John",
        "lastName": "Doe",
        "document": "12345678000190",
        "email": "johndoe.payer@example.com",
        "phone": "+5511999999999"
      },
      "installments": 1,
      "paymentMethod": "pix",
      "currency": "BRL",
      "type": "sell",
      "status": "paid",
      "statusAt": [
        { "status": "unpaid", "when": "2024-03-28T20:35:22.671Z" },
        { "status": "paid", "when": "2024-03-28T20:35:33.512Z" }
      ],
      "amount": {
        "subtotalCents": 4700,
        "subtotal": 47,
        "discountCents": 0,
        "discount": 0,
        "prorataCents": 0,
        "prorata": 0,
        "installmentFeeCents": 0,
        "installmentFee": 0,
        "totalCents": 4700,
        "total": 47
      },
      "receivers": [
        {
          "id": "platform-identity",
          "name": "Hubla Tecnologia LTDA",
          "email": "suporte@hub.la",
          "phone": "+5531999715693",
          "role": "platform",
          "paysForFees": false,
          "totalCents": 1081,
          "currency": "BRL"
        },
        {
          "id": "onVILjt4hEhXIdCrvCXBUpcBuvk2",
          "name": "Victor Lott",
          "email": "lott@hub.la",
          "phone": "+5533988891169",
          "role": "seller",
          "paysForFees": true,
          "totalCents": 3618.9999999999995,
          "currency": "BRL"
        }
      ],
      "paymentSession": {
        "ip": "220.172.165.140",
        "url": "https://pay.hub.la/inAVzweR0QYw5y03K5mq",
        "utm": {
          "source": "email",
          "medium": "active-campaign",
          "campaign": "[LEAD][QUENTE][24][MAR]"
        }
      },
      "saleDate": "2024-03-28T20:35:22.671Z",
      "dueDate": "2024-03-28T20:35:16.350Z",
      "createdAt": "2024-03-28T20:35:22.671Z",
      "modifiedAt": "2024-03-28T20:35:33.512Z",
      "version": 6
    },
    "subscriptions": [
      {
        "id": "e8bc6138-6cdb-487d-bd3a-79f246aff7db",
        "type": "recurring",
        "billingCycleMonths": 12,
        "quantity": 1
      }
    ],
    "user": {
      "id": "J0kmLJmCj1TO4m3pqW0GDlEjROQ2",
      "firstName": "John",
      "lastName": "Doe",
      "document": "12345678900",
      "email": "johndoe.user@example.com",
      "phone": "+5511999999999"
    }
  },
  "version": "2.0.0"
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Compare os dois: `products[0].id` é `9f1c4e2a-7b3d-4c8e-a1f5-2d6b8e0c3a71` na integração recomendada e `inAVzweR0QYw5y03K5mq` no modo de compatibilidade. O `product.id` (singular) é `inAVzweR0QYw5y03K5mq` nos dois. E o `version` é `"2.0.0"` nos dois.
{% endhint %}

***

## O que não muda

* **Os eventos são os mesmos.** Os seis eventos de fatura continuam existindo com os mesmos nomes: `invoice.created`, `invoice.status_updated`, `invoice.payment_succeeded`, `invoice.payment_failed`, `invoice.expired` e `invoice.refunded`.
* **Só fatura é afetada.** Os eventos de [Lead](/docs/webhooks/eventos/lead.md), [Membro](/docs/webhooks/eventos/membro.md), [Assinatura](/docs/webhooks/eventos/assinatura.md), [Parcelamento Inteligente](/docs/webhooks/eventos/parcelamento-inteligente.md) e [Solicitação de Reembolso](/docs/webhooks/eventos/solicitacao-de-reembolso.md) têm exatamente o mesmo formato nos dois modos.
* **A autenticação é idêntica.** O `x-hubla-token`, o `x-hubla-sandbox` e o `x-hubla-idempotency` não mudam. O único cabeçalho que reflete o modo é o `x-hubla-delivery-mode`, que existe justamente para você saber o formato sem olhar o corpo. Veja [Proteja seu endpoint](/docs/webhooks/proteja-seu-endpoint.md).
* **O bloco `user` é idêntico nos dois modos.** Na integração recomendada, `invoice` e `subscriptions` também trazem sempre os dados da venda real. No modo de compatibilidade isso vale para os avisos da venda, mas não para os avisos do produto, que trazem uma fatura fabricada — veja [O que vem dentro de cada aviso](#o-que-vem-dentro-de-cada-aviso).

***

## Como migrar

A troca vale para regras novas e para regras que já existem — você não precisa recriar a integração.

{% stepper %}
{% step %}

#### Revise o seu código primeiro

Antes de trocar o modo, ajuste o seu receptor. A ordem abaixo é por risco:

1. **Busca de produto** — se você usa `products[].id` para encontrar o produto no seu sistema, esse valor vai mudar. Passe a usar o id do produto.
2. **Deduplicação** — colapse os avisos duplicados de uma venda por `type` + `parentInvoiceId ?? invoice.id` + `invoice.status`. Não use `x-hubla-idempotency` para isso, e não use a identidade da venda sozinha — ela descartaria eventos legítimos da mesma venda.
3. **Soma de valores** — se você somava `amount.totalCents` de vários avisos para chegar ao valor da venda, pare. Agora chega um aviso com o total correto.
4. **Valor por oferta** *(opcional)* — se você rateava o valor por produto na sua ponta, passe a usar `products[].offers[].amountCents`.
   {% endstep %}

{% step %}

#### Valide o seu endpoint no ambiente de testes

O [ambiente de testes](/docs/webhooks/sandbox.md) serve para confirmar que o seu endpoint recebe e responde — não para conferir o formato. O payload de teste é fictício, sempre com um produto e uma oferta, e **não reflete o modo escolhido na regra**: ele não traz `hasOrderBump` e nunca mostra uma venda com order bump. Use-o para validar a conexão, e confirme o formato na primeira venda real.
{% endstep %}

{% step %}

#### Troque o modo na regra

No painel, abra a integração de Webhook, edite a regra e marque **Integração recomendada** no campo "Como enviar seus dados". Salve.
{% endstep %}

{% step %}

#### Confirme com uma venda

Acompanhe o [histórico de eventos](/docs/webhooks/historico.md) na próxima venda e confira que chegou um aviso por venda, com todos os produtos dentro. Se algo não bater, o [debugging](/docs/webhooks/debugging.md) mostra o corpo exato que enviamos.
{% endstep %}
{% endstepper %}

***

## Prazos

O modo de compatibilidade continua funcionando. Ele existe para dar tempo às ferramentas que ainda esperam o formato antigo, e não vamos desligá-lo sem aviso — quando houver data, comunicamos com antecedência por e-mail e no changelog.

Se a sua integração é sua ou você tem acesso a quem a mantém, recomendamos migrar assim que o checklist acima estiver resolvido: a integração recomendada envia menos requisições, com valores exatos e sem os avisos repetidos da mesma venda. Reenvio do **mesmo** aviso continua possível nos dois modos — é para isso que existe o `x-hubla-idempotency`.
