Skip to Content
Documentação de integração da plataforma — em evolução contínua.
PedidosReceber pedidos (webhook)

Receber pedidos (webhook)

Esta página trata dos eventos de pedido especificamente. O registro do endpoint, os limites do mecanismo e o formato geral do envelope estão em Registrar o webhook.

O caminho do pedido até o seu ERP

O diagrama responde: por onde passa um pedido entre o checkout e o meu endpoint, e por que ele chega com o nome de evento que chega?

Por isso um switch (eventType) que só cria pedido em Order_Created nunca cria nenhum: o único status que produz esse nome (100) fica abaixo do limiar de notificação, que começa em PAYMENT_ERROR (250).

E, pelo mesmo motivo, Order_Updated não significa “pedido novo”. Ele é o nome de catorze situações diferentes, da recusa de pagamento ao estorno concluído. Quem decide o que fazer é o data.status — a lista fechada está em Quais valores de data.status podem chegar.

Assinatura para pedidos

Os eventos que interessam a quem recebe pedido no ERP:

{ "eventTypes": [ "Order_Created", "Order_Updated", "Order_Cancelled", "Order_CancelRequested", "Order_StatusUpdated", "Order_InvoiceCreated", "Order_InvoiceUploaded", "Order_CreationFailed", "Order_UpdateFailed", "Order_StatusUpdateFailed", "Order_CancelFailed", "Operation_Failed" ] }

Assine a lista inteira, inclusive os eventos que “não vão chegar” — a assinatura é conferida evento a evento no momento da notificação, e o que não está na lista é descartado sem entrega, sem retentativa e sem DLQ. Assinar demais não custa nada; assinar de menos produz silêncio indistinguível de “não aconteceu”.

authToken protege o seu endpoint: a plataforma envia esse valor no header Authorization de cada entrega. Valide-o antes de processar — sem isso, qualquer um que descubra sua URL pode injetar pedidos falsos no ERP. O authType e o authToken são definidos no registro — veja Registrar o webhook.

Eventos de pedido

O que efetivamente chega no seu endpoint

CódigoEventoQuando dispara
14Order_Updatedtoda mudança de estado do pedido a partir de PAYMENT_ERROR (250): pagamento aprovado, faturamento, cancelamento, estorno, alteração de itens — e também as mudanças que o seu próprio PATCH faz. É o evento que traz o pedido novo. Os catorze valores possíveis estão em Quais valores de data.status podem chegar
118Order_StatusUpdateFailedo core recusou o seu PATCH /v1/Orders/{orderNumber}/status com um 4xx
132Order_CancelFailedo core recusou o seu POST /v1/Orders/{orderNumber}/cancel com um 4xx
999Operation_Failedo core recusou com 4xx uma operação para a qual não existe evento de falha específico — inclui PUT /v1/Orders/invoiced/{orderNumber} e POST /v1/Storages/invoice/upload

Quais valores de data.status podem chegar

Esta é a lista fechada. Um Order_Updated traz sempre um destes catorze valores em data.status — não há outros.

data.statusNomeO que aconteceuO que o seu ERP faz
250PAYMENT_ERRORpagamento recusadoignore. Não crie pedido e não reserve estoque
300PAYMENT_APPROVEDpagamento confirmadocrie o pedido. É o único valor que autoriza criar
350ORDER_INVOICEDnota registrada e valor capturadomarque como faturado
400ORDER_BEING_PREPAREDeco do seu PATCHcompare com o estado que você já tem; igual, não faça nada
500READY_FOR_PICKUP_OR_DELIVERYeco do seu PATCHidem
600SHIPPEDeco do seu PATCHidem
700DELIVEREDeco do seu PATCHidem
750ORDER_CANCELLATION_ANALYSIScancelamento em andamento, aguardando estornopare a separação; não crie pedido se o number for desconhecido
800ORDER_CANCELLEDcanceladocancele no ERP; não crie pedido se o number for desconhecido
900DELIVERY_FAILEDeco do seu PATCHcompare e ignore se igual
1000ORDER_RETURNEDeco do seu PATCHidem
1100REFUND_IN_PROGRESSestorno iniciado pela plataformaregistre o estorno em andamento
1200REFUND_COMPLETEDestorno concluído pela plataformaencerre o financeiro do pedido
1300OrderUpdatedo seu PUT /v1/Orders/{orderNumber} terminou: itens e valores foram recalculados e a cobrança ajustadanão grave 1300 como status do pedido. Releia os valores e siga com o status que você já tinha

O envelope

Todo evento — de pedido, de catálogo, de estoque ou de preço — chega com os mesmos seis campos no primeiro nível:

Campo do envelopeSempre presenteUso no seu ERP
operationIdsimcorrelaciona com o lote/operação que originou o evento — guarde-o, é a chave para investigar qualquer problema com o suporte
contractAccountIdsimtenant — valide que é o seu
relatedEntitysimtipo da entidade — Order nos eventos desta página; os demais valores são ProductWithSKUs, Category, Brand, Warehouse, WarehouseSKU, SKUPricing e User
eventTypesimo nome do evento, em texto
datasimo payload da entidade
errorsó em falhasdiagnóstico — ver Registrar o webhook

Não existe outro campo no envelope. Não há timestamp, não há assinatura HMAC, não há identificador de entrega. A chave de deduplicação sai de dentro do data — veja a seção Deduplicação, mais abaixo nesta página.

O payload de pedido

Este é o data de um Order_Updated de pedido aprovado, com os campos que importam para a integração. O payload real é maior, por três motivos:

  • campos nulos são enviados — a serialização não omite null;
  • os blocos das modalidades que você não usa chegam junto: um pedido de entrega agendada traz expressDelivery e pickupPoint como null, e acceptedOrderDetails carrega os campos de expressa e de retirada zerados ou nulos;
  • alguns blocos vêm mais completos do que o exemplo: subtotal.appliedPromotions[] traz a promoção inteira (vigência, escopo, cumulatividade) e acceptedOrderDetails traz o seller completo e o endereço do ponto de retirada.

Trate campo desconhecido como campo ignorado. Não valide o payload por lista fechada de chaves e não rejeite entrega por causa de campo a mais — a plataforma acrescenta campos sem aviso prévio.

{ "operationId": "7f3a1b2c-4d5e-4f60-8a71-9b0c1d2e3f40", "contractAccountId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "relatedEntity": "Order", "eventType": "Order_Updated", "data": { "id": "a1b2c3d4-e5f6-4708-9a1b-2c3d4e5f6071", "number": 104821, "status": 300, "createdAt": "2026-07-29T11:30:02", "updatedAt": "2026-07-29T11:32:10", "deviceId": null, "origin": 1, "deliveryType": 1, "currency": "BRL", "itemAmount": 3, "canBeCancelled": false, "invoiceUrl": null, "couponId": null, "transactionId": "125300123456", "acquirerTransactionId": "8471120993", "user": { "id": "b2c3d4e5-f607-4819-a02b-3c4d5e6f7081", "gender": "Prefiro não informar", "firstName": "João", "lastName": "Silva", "email": "joao.silva@email.com", "document": "12345678900", "phoneDDI": "55", "phoneNumber": "21999999999", "birthDate": "1985-03-14", "active": true, "contractAccountId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "contractAccount": null, "userRole": null }, "orderAddressId": "c3d4e5f6-0718-492a-b13c-4d5e6f708192", "orderAddress": { "id": "c3d4e5f6-0718-492a-b13c-4d5e6f708192", "country": "Brasil", "state": "RJ", "city": "Rio de Janeiro", "neighborhood": "Centro", "street": "Av. Rio Branco", "number": "156", "zipCode": "20031170", "addressType": "residence", "additionalInfo": "Sala 402", "reference": "Em frente à praça" }, "items": [ { "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d", "skuId": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e", "ean": "7891234567890", "reference": "MAC-ESP-500", "orderedQuantity": 2, "unitOfMeasure": "Un", "arithmeticFactor": 1, "minimumWeightForSale": null, "brandId": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "categoryId": "8d9e0f1a-2b3c-4d4e-9f5a-6b7c8d9e0f1a", "commercialPolicyId": "9c1f5a3e-7b2d-4c8e-9f01-2a3b4c5d6e7f", "promotionName": null, "salePrice": 8.99, "arithmeticFactorPrice": 8.99, "totalPerProduct": 17.98, "priceStartDateTime": null, "priceEndDateTime": null, "manualPrice": 17.98, "discountApplied": 0 }, { "id": "9e0f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b", "skuId": "0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c", "ean": "2000145000000", "reference": "PIC-ALC-KG", "orderedQuantity": 5, "unitOfMeasure": "Kg", "arithmeticFactor": 0.1, "minimumWeightForSale": 0.3, "brandId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "categoryId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e", "commercialPolicyId": "9c1f5a3e-7b2d-4c8e-9f01-2a3b4c5d6e7f", "promotionName": "Quinta da Carne", "salePrice": 39.9, "arithmeticFactorPrice": 3.99, "totalPerProduct": 19.43, "priceStartDateTime": "2026-07-29T00:00:00", "priceEndDateTime": "2026-07-31T23:59:59", "manualPrice": 17.49, "discountApplied": 1.94 } ], "orderStatusRegistry": null, "seller": { "id": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "name": "Loja Centro", "country": "Brasil", "state": "RJ", "city": "Rio de Janeiro", "neighborhood": "Centro", "zipCode": "20031170", "street": "Av. Rio Branco", "number": "156", "complement": "Loja A", "reference": null, "phoneNumber": "2133334444", "description": null, "sellerCode": "LJ-08", "active": true }, "shippingPolicy": { "id": "b3f1a2c4-5d6e-4f70-8a91-2b3c4d5e6f70", "name": "Agendada - Loja Centro", "createdAt": "2026-01-12T09:00:00", "updatedAt": null, "sellerId": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "seller": { "id": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "name": "Loja Centro", "country": "Brasil", "state": "RJ", "city": "Rio de Janeiro", "neighborhood": "Centro", "zipCode": "20031170", "street": "Av. Rio Branco", "number": "156", "complement": "Loja A", "reference": null, "phoneNumber": "2133334444", "description": null, "sellerCode": "LJ-08", "active": true }, "expressDelivery": null, "scheduledDelivery": { "id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d", "shippingPolicyId": "b3f1a2c4-5d6e-4f70-8a91-2b3c4d5e6f70", "daysLimit": 7, "minimumQuantityItems": 0, "maximumQuantityItems": 70, "minimumPurchaseValue": 50, "minimumShippingCost": 12.9, "deliveryActiveModalUndefined": false, "observations": null }, "pickupPoint": null }, "subtotal": { "subtotal": 37.41, "discount": 1.94, "couponDiscount": 0, "discountMO": 0, "appliedCouponCode": null, "frete": 12.9, "isFreeShipping": false, "total": 48.37, "giftcardDiscount": null, "appliedPromotions": [ { "id": "8b9c0d1e-2f3a-4b4c-9d5e-6f7a8b9c0d1e", "name": "Quinta da Carne", "type": 1 } ], "appliedMyOffers": [] }, "acceptedOrderDetails": { "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "sellerId": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "shippingPolicyName": "Agendada - Loja Centro", "paymentProvider": "MercadoPago", "paymentMethod": "CreditCard", "userPaymentData": "{\"cardToken\":\"\",\"cardBrand\":\"Visa\",\"lastFourDigits\":\"6411\"}", "installments": 1, "subtotal": 37.41, "discount": 1.94, "netDiscount": 1.94, "frete": 12.9, "total": 48.37, "couponDiscount": 0, "appliedCouponCode": null, "cashChange": 0, "scheduledDate": "2026-07-30", "scheduledDayOfWeek": 4, "scheduledStartTime": "14:00:00", "scheduledEndTime": "18:00:00", "expressStartDate": null, "expressEndDate": null, "pickupPointName": null, "hasGiftcardApplied": false, "giftcardDiscount": null, "transactionId": "125300123456", "brand": "Visa", "additionalInformation": [ { "title": "Quem irá receber as compras?", "value": "Eu mesmo", "dataType": 1 }, { "title": "Aceita substituição de item?", "value": false, "dataType": 2 } ] }, "paymentAttempts": [ { "serviceProviderPaymentMethod": { "id": "2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b", "paymentMethod": { "id": 1, "name": "CreditCard" }, "contractServiceProvider": { "name": "MercadoPago" } }, "status": "Success", "type": "Authorize", "brand": "Visa", "data": { "id": 125300123456, "status": "approved", "status_detail": "accredited" }, "transactionDate": "2026-07-29T11:32:08", "transactionId": "125300123456", "acquirerTransactionId": "8471120993", "paymentId": "0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a", "value": 48.37, "authCode": "112233" } ], "orderInvoiced": null, "giftcard": { "applied": false, "appliedAmount": 0 } } }
Campo do itemConteúdo
items[].referenceo reference que você enviou no SKU em POST /v1/batch/Products
items[].eano EAN do SKU
items[].skuIdGUID interno do SKU na plataforma
items[].idGUID interno do produto (não do item do pedido)
items[].orderedQuantitysempre um inteiro. Em item de unidade, a quantidade de unidades. Em item pesável, a quantidade de múltiplos do arithmeticFactor, arredondada — não o peso
items[].unitOfMeasureUn, Kg, G ou Mg — nome, não código
items[].arithmeticFactoro passo de venda do item pesável (0.1 = de 100 em 100 g)
items[].salePricepreço unitário vigente — no pesável, o preço por unitOfMeasure (por quilo, em Kg)
items[].totalPerProductpreço × quantidade real, antes de rateio de promoção
items[].manualPricevalor do item depois do rateio de desconto — é o que soma no total
items[].discountApplieddesconto rateado neste item

orderedQuantity nunca é o peso. O campo é inteiro; o peso é convertido para múltiplos do arithmeticFactor e arredondado antes de sair. No exemplo acima, 487 g de picanha com arithmeticFactor: 0.1 chegam como "orderedQuantity": 5, não como 0.487.

Um ERP que trate esse campo como quilo fatura 5 kg de picanha. Um ERP que o trate como unidade fatura 5 peças. Os dois erram, e erram em todo item pesável do pedido.

  • Peso aproximado = orderedQuantity × arithmeticFactor (aqui, 0,5 kg) — é o passo de venda, já arredondado.
  • O peso exato separado não vem no payload. Quem carrega o valor real é totalPerProduct (19.43 = 0,487 kg × R$ 39,90) e manualPrice. Para cobrança e conferência fiscal, use esses campos, nunca orderedQuantity × salePrice.

O item do pedido não traz skuExternalReference nem o nome do produto. O campo de vínculo com o seu catálogo é items[].reference (o reference do SKU) ou items[].ean — não o externalReference.

Isso importa porque PUT /v1/Orders/{orderNumber} exige skuExternalReference para informar a separação. Se, no seu ERP, reference e externalReference do SKU não são o mesmo valor, guarde os dois no cadastro no momento da carga de catálogo: o webhook devolve um e a alteração de pedido pede o outro. Veja Alterar itens do pedido.

Como ler os valores dentro de data

Enums são número e datas não têm fuso.

status (300), deliveryType (1 agendada, 2 expressa, 3 retirada) e origin (0 indefinido, 1 app, 2 web) chegam como inteiros. Só o eventType e o relatedEntity do envelope vêm como texto. A tabela de códigos de status está em Ciclo de vida do pedido.

A exceção dentro de items[] é unitOfMeasure, que vem como texto ("Kg") no webhook e como número (1) em GET /v1/gateway/orders/{id}/details, no core — dois serviços, duas configurações de serialização, as duas formas esperadas.

As datas dentro de data são hora local do Brasil (UTC−3), sem sufixo de fuso ("2026-07-29T11:32:10"). Um parser que assume UTC desloca o pedido em três horas.

Deduplicação

O envelope não carrega identificador de entrega, e o operationId não é único por notificação: um lote de 500 produtos gera 500 notificações com o mesmo operationId.

Deduplicar só por operationId descarta 499 dos 500 eventos de um lote de catálogo. Foi o erro mais comum nas primeiras integrações.

A chave canônica, válida para todos os tipos de evento, é:

operationId + eventType + identidade da entidade dentro de data

O diagrama responde: o que o meu endpoint faz com cada entrega recebida?

O ramo da esquerda é o que a maioria das integrações erra: duplicata também responde 200. Responder 4xx ou 5xx para um evento que você já processou faz a plataforma retentar — e são só 3 entregas antes da DLQ silenciosa.

relatedEntityIdentidade dentro de data
Orderdata.number
ProductWithSKUsdata.product.externalReference
Brand, Categorydata.externalReference
Warehousedata.id
SKUPricingdata.skuId + data.commercialPolicyId
Userdata.email (o id é removido do payload de usuário por segurança)
WarehouseSKUnão tem campo escalar de identidade — use a regra de hash abaixo

A regra única, que dispensa a tabela e cobre inclusive WarehouseSKU:

operationId + eventType + sha256(JSON.stringify(data))

As retentativas reentregam a mesma mensagem byte a byte, então o hash é estável entre elas.

Verificar o payload de um pedido

Se você perdeu uma entrega, pode buscar o payload que teria sido enviado:

curl $GROCERS_API/v1/Orders/104821/webhook-payload \ -H "Authorization: Basic $BASIC" -H "x-contractAccountId: $TENANT"

A resposta da v2 é o objeto que originaria o data do webhook, mas ele não é idêntico ao data entregue no seu endpoint. Duas diferenças importam:

CampoNo webhookNa v2
contractAccountIdfica no envelope, fora do datavem dentro do objeto
eventTypevem no envelope, como texto ("Order_Updated")vem dentro do objeto, como número (14)

Não há operationId nem relatedEntity — esses só existem no envelope da notificação. E orderStatusRegistry e deviceId vêm null, igual ao webhook.

{ "id": "a1b2c3d4-e5f6-4708-9a1b-2c3d4e5f6071", "contractAccountId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "eventType": 14, "number": 104821, "status": 300, "createdAt": "2026-07-29T11:30:02", "deviceId": null, "orderStatusRegistry": null, "user": { "id": "b2c3d4e5-f607-4819-a02b-3c4d5e6f7081", "firstName": "João", "lastName": "Silva", "document": "12345678900" }, "items": [ { "skuId": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e", "ean": "7891234567890", "reference": "MAC-ESP-500", "orderedQuantity": 2, "unitOfMeasure": "Un", "salePrice": 8.99, "totalPerProduct": 17.98, "manualPrice": 17.98, "discountApplied": 0 } ], "seller": { "id": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "name": "Loja Centro" }, "shippingPolicy": { "id": "b3f1a2c4-5d6e-4f70-8a91-2b3c4d5e6f70", "name": "Agendada - Loja Centro" }, "subtotal": { "subtotal": 37.41, "discount": 1.94, "frete": 12.9, "total": 48.37 }, "acceptedOrderDetails": { "paymentMethod": "CreditCard", "total": 48.37 }, "paymentAttempts": [ { "status": "Success", "type": "Authorize", "value": 48.37 } ], "currency": "BRL" }
CódigoSignificado
200payload devolvido
404pedido não encontrado, ou ainda em status que não gera webhook (CART_CREATED / PaymentInProgress)
500falha interna

O exemplo acima é o da v2. A v1 lê de uma base de leitura separada, com serviço e serialização próprios: o formato da resposta dela não é garantido igual ao da v2. Se você vai depender dessa rota em automação, escolha uma das duas versões e valide o formato dela contra um pedido real — não presuma que trocar a versão na URL devolve o mesmo objeto.

Se as duas divergirem no conteúdo, a v2 é a fonte de verdade (ela monta o payload do zero a partir do core) e a divergência é assunto de chamado.

Nenhuma das duas rotas reenvia a notificação — elas só mostram o payload.