Status de sync do menu
curl --request POST \
--url https://app.fire.rest/api/v1/webhooks/xmart/sync-status \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"vendorId": "<string>",
"type": "<string>",
"status": "<string>",
"message": "<string>",
"eventId": "<string>"
}
'import requests
url = "https://app.fire.rest/api/v1/webhooks/xmart/sync-status"
payload = {
"vendorId": "<string>",
"type": "<string>",
"status": "<string>",
"message": "<string>",
"eventId": "<string>"
}
headers = {
"x-api-key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
vendorId: '<string>',
type: '<string>',
status: '<string>',
message: '<string>',
eventId: '<string>'
})
};
fetch('https://app.fire.rest/api/v1/webhooks/xmart/sync-status', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.fire.rest/api/v1/webhooks/xmart/sync-status",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'vendorId' => '<string>',
'type' => '<string>',
'status' => '<string>',
'message' => '<string>',
'eventId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.fire.rest/api/v1/webhooks/xmart/sync-status"
payload := strings.NewReader("{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.fire.rest/api/v1/webhooks/xmart/sync-status")
.header("x-api-key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.fire.rest/api/v1/webhooks/xmart/sync-status")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": { "ok": true, "updated": 2 }
}
{
"success": true,
"data": { "ok": true, "updated": 0 }
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "status must be one of SUCCESS, FAILED"
}
{
"success": false,
"error": "UNAUTHORIZED",
"message": "API key required. Use x-api-key: pk_live_... header"
}
{
"success": false,
"error": "FORBIDDEN",
"message": "vendorId 100.6.1350 does not belong to this key's vendor"
}
{
"success": false,
"error": "DOMAIN_ERROR",
"message": "Failed to finalize sync logs for vendor 100.6.1350"
}
{
"success": false,
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred. Retry the request."
}
API
Status de sync do menu
Endpoint de entrada para o qual seu integrador faz POST quando termina de publicar o menu/produtos de um vendor. O Fire finaliza os sync logs pendentes do vendor e recalcula o status de sync do menu. Um status por vendor por padrão (tudo ou nada), a menos que você envie eventId para atingir um único assignment de agregador.
POST
/
api
/
v1
/
webhooks
/
xmart
/
sync-status
Status de sync do menu
curl --request POST \
--url https://app.fire.rest/api/v1/webhooks/xmart/sync-status \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"vendorId": "<string>",
"type": "<string>",
"status": "<string>",
"message": "<string>",
"eventId": "<string>"
}
'import requests
url = "https://app.fire.rest/api/v1/webhooks/xmart/sync-status"
payload = {
"vendorId": "<string>",
"type": "<string>",
"status": "<string>",
"message": "<string>",
"eventId": "<string>"
}
headers = {
"x-api-key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
vendorId: '<string>',
type: '<string>',
status: '<string>',
message: '<string>',
eventId: '<string>'
})
};
fetch('https://app.fire.rest/api/v1/webhooks/xmart/sync-status', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.fire.rest/api/v1/webhooks/xmart/sync-status",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'vendorId' => '<string>',
'type' => '<string>',
'status' => '<string>',
'message' => '<string>',
'eventId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.fire.rest/api/v1/webhooks/xmart/sync-status"
payload := strings.NewReader("{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.fire.rest/api/v1/webhooks/xmart/sync-status")
.header("x-api-key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.fire.rest/api/v1/webhooks/xmart/sync-status")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"vendorId\": \"<string>\",\n \"type\": \"<string>\",\n \"status\": \"<string>\",\n \"message\": \"<string>\",\n \"eventId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": { "ok": true, "updated": 2 }
}
{
"success": true,
"data": { "ok": true, "updated": 0 }
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "status must be one of SUCCESS, FAILED"
}
{
"success": false,
"error": "UNAUTHORIZED",
"message": "API key required. Use x-api-key: pk_live_... header"
}
{
"success": false,
"error": "FORBIDDEN",
"message": "vendorId 100.6.1350 does not belong to this key's vendor"
}
{
"success": false,
"error": "DOMAIN_ERROR",
"message": "Failed to finalize sync logs for vendor 100.6.1350"
}
{
"success": false,
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred. Retry the request."
}
Este endpoint é de entrada: seu integrador (o sistema que publica o catálogo do Fire downstream, um agregador, um cliente XMART, um POS, etc.) faz POST nele assim que termina a publicação assíncrona dos produtos de um vendor. O Fire autentica a requisição, resolve o vendor, finaliza todos os sync logs que ficaram pendentes para esse vendor e recalcula o
O endpoint é seguro de reexecutar em todos esses casos: uma retentativa após uma finalização bem-sucedida é um no-op limpo (
syncStatus do menu afetado. Este é o callback que fecha o ciclo aberto por um evento de publicação de menu/produtos que carrega autoPublish.
Este endpoint é síncrono. O Fire autentica, resolve o vendor, finaliza os sync logs pendentes, recalcula o status do menu e responde
200 OK com a quantidade de linhas que alterou (updated). Não há fila nem polling; diferente dos webhooks de status do pedido do agregador e KDS, o trabalho já está feito quando a resposta retorna.O id de correlação é opcional. Se você enviar
eventId (somente canais agregador), o Fire fecha apenas a linha de sync daquele assignment. Se você omiti-lo, a única chave é vendorId, e o Fire aplica o resultado a todas as linhas de sync pendentes do vendor para a entidade resolvida a partir de type (tudo ou nada): se o vendor tinha vários assignments e apenas alguns falharam, você ainda envia um único FAILED, e todas as linhas pendentes desse vendor vão para FAILED, porque sem eventId o Fire não consegue saber qual assignment falhou. Nesse caso, a correção se apoia no lock por vendor do Fire, já que nunca há mais de uma leva de publicação pendente para um vendor por vez. Veja Correlação e o lock do vendor.Autenticação
Este endpoint requer uma API key vendor-scoped com o escopowebhooks:xmart (binding de account + vendor). O Fire exige que o vendorId do body pertença a esse vendor. Keys sem o escopo, ou sem binding de vendor, são rejeitadas com 403 Forbidden.
string
obrigatório
Sua API key do Fire vendor-scoped com escopo
webhooks:xmart. Gere uma em Developers → API Management para o account/vendor cujos resultados de sync esta key pode reportar.string
Opcional
Bearer <token>, aceito como alternativa legacy ao x-api-key. Envie um ou outro.Corpo da requisição
string
obrigatório
O identificador do vendor no formato dotted (ex.
100.6.1350), o mesmo valor que o Fire usa nos sync logs e nas lojas. Deve pertencer ao vendor vinculado à sua API key.Os payloads de publicação de saída carregam o id do vendor numérico legacy (ex.
1350) dentro de list. Este callback é diferente: envie o id dotted.string
obrigatório
Qual entidade seu integrador publicou. Comparado sem diferenciar maiúsculas/minúsculas.
PRODUCTSfinaliza ambas as dimensões do vendor, a de menu e a de produtos; o sync de menu empurra os produtos do vendor downstream, e seu integrador reporta o resultado comoPRODUCTS.- Qualquer outro valor (
STORES, …) finaliza apenas as linhas pendentes daquela entidade.
string
obrigatório
O resultado da publicação para o vendor. Enum:
SUCCESS | FAILED.string
Detalhe opcional (motivo da falha, trace do provedor). Armazenado nas linhas finalizadas como o detalhe de erro quando
status é FAILED; ignorado (e limpo) quando status é SUCCESS. Se omitido com status em FAILED, o Fire armazena uma mensagem de falha genérica padrão.string
O
event.id do envelope Fire que seu integrador recebeu para esse assignment (menu.updated / product.updated), formato evt_<12 hex> (ex. evt_9f2c41ab77de). Um por assignment (loja × canal × fulfillment).Quando presente, o Fire fecha apenas a linha de sync daquele assignment, em vez de varrer todas as linhas pendentes do vendor. Somente canais agregador: omita para outros integradores, que recebem a varredura por vendor descrita acima.type — o que é finalizado
type (sem diferenciar maiúsculas) | Entidades finalizadas | Por quê |
|---|---|---|
PRODUCTS | menu e produtos | O sync de menu empurra os produtos do vendor downstream; seu integrador reporta o resultado como PRODUCTS, e ambas as dimensões do vendor fecham nesse único callback. |
qualquer outro (STORES, …) | apenas aquela entidade | Cada tipo fecha somente suas próprias linhas pendentes. |
Exemplos
PRODUCTS — publicação bem-sucedida
{
"vendorId": "100.6.1350",
"type": "PRODUCTS",
"status": "SUCCESS"
}
PRODUCTS — publicação falhou (com detalhe)
{
"vendorId": "100.6.1350",
"type": "PRODUCTS",
"status": "FAILED",
"message": "Product 4471 rejected: missing tax info"
}
PRODUCTS — canal agregador, um único assignment falhou (eventId)
{
"vendorId": "100.6.1350",
"type": "PRODUCTS",
"status": "FAILED",
"eventId": "evt_9f2c41ab77de",
"message": "Product 4471 rejected: missing tax info"
}
O que o Fire faz
Uma vez autenticado e resolvido, o Fire, em uma única passagem síncrona:- Se
eventIdestiver presente, resolve a única linha de sync pendente que corresponde, comvendorIdcomo guard. Caso contrário, resolve as entidades afetadas a partir detype(PRODUCTS→ menu e produtos; qualquer outro → aquela entidade somente) e mira em todas as linhas pendentes do vendor para essas entidades. - Finaliza a(s) linha(s) resolvida(s): define o
statusque você enviou, carimba o momento de conclusão e registramessagecomo o detalhe de erro quando éFAILED. A quantidade de linhas alteradas é retornada comoupdated. - Se não havia linhas pendentes, é um no-op idempotente →
200comupdated: 0. - Recalcula o
syncStatusdo menu afetado a partir de suas linhas finalizadas:- alguma dimensão ainda pendente →
PENDING - todas terminais e todas bem-sucedidas →
SYNCED - todas terminais e alguma falha →
FAILED
- alguma dimensão ainda pendente →
- Finalizar as linhas libera o lock do vendor: não há mais linhas pendentes para essas entidades, então a próxima leva de publicação do vendor pode começar.
Resposta
No sucesso o endpoint retorna200 OK com a quantidade de linhas de sync que alterou, envolvida no envelope padrão de sucesso do Fire. Um 200 significa que o trabalho está feito: os logs estão finalizados e o status do menu recalculado.
boolean
Sempre
true em uma resposta 200.boolean
Sempre
true quando a requisição foi processada.number
Quantas linhas de sync pendentes foram finalizadas. Sem
eventId, pode ser qualquer quantidade entre as linhas pendentes do vendor; 0 em um no-op idempotente. Com eventId, é 0 ou 1: no máximo a única linha de assignment que correspondeu.{
"success": true,
"data": { "ok": true, "updated": 2 }
}
{
"success": true,
"data": { "ok": true, "updated": 0 }
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "status must be one of SUCCESS, FAILED"
}
{
"success": false,
"error": "UNAUTHORIZED",
"message": "API key required. Use x-api-key: pk_live_... header"
}
{
"success": false,
"error": "FORBIDDEN",
"message": "vendorId 100.6.1350 does not belong to this key's vendor"
}
{
"success": false,
"error": "DOMAIN_ERROR",
"message": "Failed to finalize sync logs for vendor 100.6.1350"
}
{
"success": false,
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred. Retry the request."
}
Uma falha ao finalizar linhas ou recalcular o status do menu volta como
400 DOMAIN_ERROR, não 500, mesmo sendo uma falha do lado servidor (ex. um erro transitório de banco de dados). Veja Idempotência e retentativas: uma política ingênua de “retentar somente diante de 5xx” não vai retentar esse caso.Idempotência e retentativas
O callback é seguro para reexecutar. Ele se chaveia sobre as linhas pendentes do vendor, então:| Cenário | Resultado |
|---|---|
| Primeira entrega, havia linhas pendentes | 200 com updated: N (linhas finalizadas) |
| Mesmo callback reenviado após já ter finalizado | 200 com updated: 0 (no-op idempotente) |
Falta vendorId / type / status, ou status fora do enum | 400 VALIDATION_ERROR, não retentável, o body está malformado |
| Falha ao finalizar ou recalcular (ex. erro transitório de banco de dados) | 400 DOMAIN_ERROR, retentável |
Key sem webhooks:xmart, ou vendorId não é o vendor desta key | 403 |
| Erro não tratado | 500 INTERNAL_SERVER_ERROR, retentável |
Retentar somente diante de
5xx não é suficiente. A falha transitória mais provável, o Fire falhar ao finalizar as linhas, volta como 400 DOMAIN_ERROR, não 500. Retente também diante desse código. 400 VALIDATION_ERROR é o único 400 que você não deve retentar: significa que o body em si é inválido.updated: 0), e uma retentativa após DOMAIN_ERROR tenta novamente a mesma finalização.
Correlação e o lock do vendor
ComeventId (canais agregador): o Fire faz o match da linha diretamente por esse id, com vendorId como guard. Sem ambiguidade: o callback fecha exatamente o assignment ao qual se refere.
Sem eventId: a única chave é vendorId. O Fire se apoia em um lock por vendor: enquanto uma leva de publicação está em andamento, as linhas do vendor ficam pendentes e nenhuma segunda leva pode começar, então todas as linhas pendentes do vendor pertencem à leva que este callback finaliza.
O lock é indefinido: apenas um callback que finalize o libera. Se o callback nunca chegar, o vendor fica bloqueado (sua próxima publicação não pode começar) até intervenção manual. Sempre envie o callback, mesmo diante de
FAILED.Relação com o fluxo de publicação
Este callback é a contraparte da flagautoPublish que o Fire define no último request de publicação por vendor de um evento de publicação de menu/produtos. Essa flag diz ao seu integrador para publicar a leva downstream; quando a publicação termina, seu integrador reporta o resultado aqui para que o Fire finalize os sync logs e recalcule o syncStatus do menu.
Relacionado
Publicação de menu
O fluxo de publicação que este callback fecha: como o Fire emite o menu/produtos que um vendor deve publicar.
Status do pedido do agregador
O webhook de entrada irmão para o status de entrega, mesmo modelo de auth, mas assíncrono (fila + polling).
Menu atualizado
O evento de saída de publicação de menu que carrega o catálogo a publicar.
Autenticação
Como funcionam as API keys, escopos e o binding de vendor.

