Eventos de Webhook¶
Webhooks são notificações HTTP POST enviadas pela Gubee para um endpoint seu sempre que um evento de negócio acontece (pedido criado, estoque alterado, preço atualizado, etc.). Eles eliminam a necessidade de polling e reduzem a latência entre o fato no marketplace e o processamento no seu ERP.
Quando usar webhook vs. polling¶
Use webhook como via primária de integração: ele é push, em tempo real e dispara apenas quando há mudança. Use polling (fila de pedidos) apenas como fallback ou reconciliação — quando o seu endpoint ficar fora do ar, quando você perder um evento ou quando quiser garantir consistência eventual.
A Gubee mantém uma fila de pedidos consultável em GET /integration/orders/queue/{status} projetada exatamente para esse cenário de recuperação. Veja o detalhamento no guia de jornada Receber pedidos no ERP.
| Critério | Webhook | Fila (polling) |
|---|---|---|
| Direção | Push (Gubee → você) | Pull (você → Gubee) |
| Latência | Segundos | Definida pelo seu scheduler |
| Esforço de implementação | Médio (endpoint público, idempotência) | Baixo (job que consome fila) |
| Recomendado para | Produção, tempo real | Fallback, reprocessamento, teste |
Em produção madura, use os dois: webhook como via principal e um job periódico lendo a fila para reconciliar eventuais falhas.
Como assinar eventos¶
A configuração de webhooks é feita no painel administrativo da Gubee, não por API:
- Acesse o painel Gubee com usuário administrador.
- Vá em Minha Conta → Notificações de Eventos.
- Cadastre a URL do endpoint que vai receber os
POSTs. A URL precisa ser pública, aceitar HTTPS e responder2xxem até 5 segundos. - Dê um nome (
urlName) para esse endpoint — você vai usar esse nome para recuperar notificações perdidas (veja retry-policy.md). - Marque os status que você quer receber (assinatura por status, não por domínio inteiro).
Dica para ERPs de marketplace¶
ERPs que só cuidam do fluxo de venda não precisam assinar todos os status. Para o ciclo clássico de pedido, assine apenas:
- Faturado — dispara a emissão da NF-e no seu ERP.
- Enviado — dispara o rastreio/logística reversa.
- Entregue — fecha o ciclo e habilita pós-venda.
- Cancelado — apenas se o seu hub puder cancelar/estornar; caso contrário, ignore.
Status como Criado e Pago são úteis para reservas de estoque antecipadas, mas na maioria dos fluxos o Faturado já é o gatilho suficiente. Quanto menos eventos você assinar, menos ruído e menos deduplicação no seu lado.
Catálogo de eventos¶
Os eventos se dividem em quatro domínios (domain). O campo itemId muda de significado conforme o domínio.
| Domain | type |
Gatilho | itemId significa |
|---|---|---|---|
order |
OrderCreated |
Novo pedido criado na Gubee | orderId |
order |
OrderPayed |
Pedido marcado como pago | orderId |
order |
OrderInvoiced |
Nota fiscal anexada ao pedido | orderId |
order |
OrderShipped |
Pedido marcado como enviado | orderId |
order |
OrderDelivered |
Pedido marcado como entregue | orderId |
order |
OrderCanceled |
Pedido cancelado | orderId |
order |
OrderReturned |
Pedido devolvido | orderId |
order |
OrderShipmentException |
Exceção de entrega (cliente não encontrado, endereço inválido, etc.) | orderId |
product |
VariationCreated |
Nova variação criada | skuId |
product |
VariationUpdated |
Variação atualizada | skuId |
price |
PriceUpdated |
Preço alterado | ID interno de preço (use relatedId para o skuId) |
stock |
StockUpdated |
Estoque alterado | ID interno de estoque (use relatedId para o skuId) |
Status disponíveis para assinatura no painel: Criado, Pago, Faturado, Enviado, Entregue, Concluído, Retornado, Cancelado, Exceção na entrega.
Para
priceestock, oitemIdé um identificador interno da Gubee — não tente usá-lo diretamente. OskuIdrelevante vem no camporelatedId.
Exemplos de payload por domínio¶
Pedido (order)¶
{
"resource": "/integration/orders/1234567890",
"domain": "order",
"sellerId": "5f3e2a1b2c3d4e5f6a7b8c9d",
"itemId": "1234567890",
"type": "OrderInvoiced",
"attempts": 1,
"sent": "2022-06-15T17:14:50.432503Z",
"received": "2022-06-15T17:13:07.555760Z"
}
Produto (product)¶
{
"resource": "/integration/products/variations/sku-ABC123",
"domain": "product",
"sellerId": "5f3e2a1b2c3d4e5f6a7b8c9d",
"itemId": "sku-ABC123",
"type": "VariationUpdated",
"attempts": 1,
"sent": "2022-06-15T17:14:50.432503Z",
"received": "2022-06-15T17:13:07.555760Z"
}
Preço (price)¶
{
"resource": "/integration/prices/654321",
"domain": "price",
"sellerId": "5f3e2a1b2c3d4e5f6a7b8c9d",
"itemId": "654321",
"relatedId": "sku-ABC123",
"type": "PriceUpdated",
"attempts": 1,
"sent": "2022-06-15T17:14:50.432503Z",
"received": "2022-06-15T17:13:07.555760Z"
}
Estoque (stock)¶
{
"resource": "/integration/stock/987654",
"domain": "stock",
"sellerId": "5f3e2a1b2c3d4e5f6a7b8c9d",
"itemId": "987654",
"relatedId": "sku-ABC123",
"type": "StockUpdated",
"attempts": 1,
"sent": "2022-06-15T17:14:50.432503Z",
"received": "2022-06-15T17:13:07.555760Z"
}
O campo resource é o caminho absoluto na API de integração que devolve a entidade completa. Use-o para buscar o estado atualizado logo ao receber o webhook:
Veja a referência completa dos campos em payload-schema.md.
Filtro de contexto é responsabilidade de quem recebe¶
Importante: a Gubee dispara eventos para todos os itens relevantes ao sellerId, mesmo quando o item não pertence ao contexto do marketplace que o receptor integra. Por exemplo: se você tem uma integração exclusiva com Mercado Livre, vai receber eventos de pedidos do SHopee, Magalu, Amazon, etc., mesmo que não vá processá-los.
Isso é por design — a Gubee não conhece o mapeamento "este webhook = este marketplace". O receptor deve filtrar por contexto. A forma usual é consultar o pedido/produto via resource e inspecionar o campo de canal.
Exemplo de handler que filtra por contexto (pseudo-Kotlin):
@PostMapping("/gubee/webhook")
fun handle(@RequestBody event: WebhookEvent): ResponseEntity<Void> {
// 1. Busca a entidade completa usando o campo resource
val entity = gubeeClient.fetch(event.resource)
// 2. FILTRO DE CONTEXTO: ignora pedidos de marketplaces que você não integra
if (event.domain == "order") {
val order = entity as Order
if (order.channel ! in SUPPORTED_CHANNELS) {
// Não é seu contexto — apenas acuse recebimento e abandone
return ResponseEntity.ok().build()
}
}
// 3. Idempotência (ver idempotency.md)
val dedupKey = "${event.itemId}|${event.type}|${event.received}"
if (dedupRepository.existsByKey(dedupKey)) {
return ResponseEntity.ok().build()
}
dedupRepository.save(DedupEntry(key = dedupKey, receivedAt = Instant.now()))
// 4. Processamento de fato
when (event.type) {
"OrderInvoiced" -> invoiceService.sync(order)
"OrderShipped" -> shippingService.dispatch(order)
"OrderCanceled" -> orderService.cancel(order)
// ...
}
return ResponseEntity.ok().build()
}
Sem esse filtro, você vai processar (e possivelmente duplicar no ERP) pedidos que não são da sua alçada. Sempre acuse 2xx mesmo para eventos ignorados — caso contrário a Gubee vai retentar e poluir sua fila de perdidas.
Próximos passos¶
- Schema do payload — referência campo a campo.
- Política de retentativa — como funciona o backoff e como recuperar notificações perdidas.
- Idempotência — por que você precisa de tabela de dedup e como modelá-la.