Documentação da API

Guia completo para integrar seu sistema de delivery com o iFood através do iHub. Configure webhooks, vincule lojas e gerencie pedidos.

Introdução

O iHub atua como intermediário entre a API do iFood e o seu sistema de delivery. Após configurar seu webhook no painel, o iHub cuida de todo o polling, autenticação e reenvio — seu sistema só precisa receber requisições POST.

O fluxo funciona assim:

  1. Você registra sua conta e assina o plano.
  2. No painel, configura o domínio e a URL de webhook do seu sistema.
  3. Inicia o processo de vinculação de uma loja iFood (merchant) via API.
  4. A partir daí, o iHub envia todos os pedidos e eventos diretamente para seu webhook.
  5. Use o módulo Merchant para consultar status da loja, pausas, horários e demais operações da API de lojas do iFood.

Base URL

Todas as requisições à API devem usar o prefixo:

https://ihub.arcn.com.br/api

Autenticação

Todas as rotas protegidas exigem o header Authorization: Bearer {token}.

Chamadas do seu sistema para o iHub

Use o token secreto (UUID) exibido no painel após salvar as configurações. Esse token identifica seu cadastro na tabela de clientes.

Segurança: trate o token como senha. Não commite em repositórios públicos; use variáveis de ambiente no seu servidor.

Webhooks do iHub para o seu sistema

Na direção oposta (iHub → sua URL de webhook), o iHub envia o mesmo valor no header X-iFood-Hub-Signature para você validar a origem da requisição.

Validação no seu endpoint
$hubSignature = $request->header('X-iFood-Hub-Signature');
// Compare com o token secreto que você armazenou (mesmo valor do painel)

if ($hubSignature !== $seuTokenArmazenadoComSeguranca) {
    return response()->json(['message' => 'Unauthorized'], 401);
}

Ambiente sandbox

O sandbox existe para você integrar o sistema de delivery antes de autorizar uma loja real no iFood. Com o modo ativo, as rotas de produção (pedidos, merchant, shipping, reviews, analytics, OAuth) devolvem dados virtuais persistidos na sua conta — nenhuma chamada vai para a API do iFood.

Ative no painel (aba Sandbox) ou via POST /api/sandbox/bootstrap. A loja virtual ganha um merchant_id estável. O módulo Catalog não é simulado (é o fluxo mais complexo da API real).

Importante: enquanto o sandbox estiver ligado, tokens de lojas reais desta conta também são redirecionados para o ambiente virtual. Desative o modo nas configurações quando for operar produção.

Fluxo recomendado

  1. Cadastre domínio, webhook e copie o token secreto.
  2. Ative o sandbox no painel (cria a loja virtual automaticamente).
  3. Crie um pedido: POST /api/sandbox/orders — o iHub envia PLC com order_details para o seu webhook.
  4. No seu sistema, confirme com POST /api/ifood/action (action=confirm). O sandbox avança o status e dispara CFM.
  5. Siga o ciclo: startPreparation → readyToPickup → dispatch. Códigos de teste: retirada 8472, entrega 5482, drop 3321.

Rotas de controle do sandbox

Todas exigem Authorization: Bearer {secret_token}.

MétodoRotaDescrição
POST/api/sandbox/bootstrapAtiva o modo e cria a loja virtual.
GET/api/sandboxEstado atual: loja, pedidos, reviews, eventos disponíveis.
POST/api/sandbox/ordersCria um pedido (scenario opcional) e dispara PLC.
POST/api/sandbox/trigger-eventDispara um evento (code, order_id opcional).
POST/api/sandbox/resetLimpa pedidos e estado simulado. Mantém a loja.
Exemplo — criar pedido delivery iFood
curl -X POST https://ihub.arcn.com.br/api/sandbox/orders \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scenario":"delivery_ifood"}'

Cenários de pedido

scenarioO que simula
delivery_ifoodEntrega pela frota iFood, pago online (padrão).
delivery_merchantEntrega própria, com pickupCode.
takeoutRetirada no balcão.
indoorConsumo no local (mesa).
scheduledPedido agendado.
cashPagamento em dinheiro na entrega.
handshakePedido com disputa de reembolso aberta.
shipping_easyUse também POST /api/shipping/{id}/orders para Easy Delivery.

Eventos extras que você pode disparar: HSA (disputa), ADR (entregador), DDCR (código de entrega), DACR/DACA/DACD (troca de endereço), além do ciclo PLC → CFM → PSP → RTP → DSP → CON/CAN.

Gerar User Code

Primeiro passo para vincular uma loja iFood. Gera um código que o lojista deve inserir na página de vinculação do iFood.

POST /api/auth/generate-user-code

Exemplo de requisição

cURL
curl -X POST https://ihub.arcn.com.br/api/auth/generate-user-code \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON
{
    "userCode": "ABCD-EFGH",
    "authorizationCodeVerifier": "um-codigo-verificador-longo",
    "verificationUrl": "https://portal.ifood.com.br/...",
    "verificationUrlComplete": "https://portal.ifood.com.br/...?code=ABCD-EFGH"
}
Fluxo: Exiba o userCode ou redirecione o lojista para verificationUrlComplete. Após o lojista autorizar no portal do iFood, use authorizationCodeVerifier na próxima etapa.

Vincular Merchant

Após o lojista autorizar o acesso no portal do iFood, envie os dados de autorização para vincular a loja ao seu sistema.

POST /api/auth/link-merchant

Parâmetros

CampoTipoDescrição
domainstringDomínio do seu sistema cadastrado no painel.
authorizationCodestringCódigo de autorização retornado pelo iFood após o lojista aprovar.
authorizationCodeVerifierstringVerificador obtido na etapa anterior (generate-user-code).
merchantIdstring (UUID) · opcionalUUID da loja a vincular. Obrigatório quando o token OAuth inclui mais de uma loja ainda não vinculada. Se omitido, o iHub infere a loja pelo merchant_scope do JWT (loja nova em relação às já vinculadas).

Exemplo de requisição

cURL
curl -X POST https://ihub.arcn.com.br/api/auth/link-merchant \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "authorizationCode": "codigo-do-ifood",
    "authorizationCodeVerifier": "verificador-da-etapa-1"
  }'

Resposta de sucesso

JSON
{
    "message": "Merchant linked successfully",
    "merchant": {
        "id": 1,
        "client_id": 5,
        "merchant_id": "abcd-1234-efgh-5678",
        "access_token": "...",
        "refresh_token": "...",
        "expires_at": "2026-04-15T00:00:00.000000Z"
    },
    "ifood_details": {
        "id": "abcd-1234-efgh-5678",
        "name": "Restaurante Exemplo",
        "corporateName": "..."
    }
}
Pronto! A partir deste momento, o iHub começa a monitorar os pedidos e eventos desta loja e os envia para o webhook configurado no seu painel.

Vincular todas as lojas do token

Quando o lojista autoriza acesso a várias lojas de uma vez (ou o token OAuth já inclui múltiplos merchants), use este endpoint para vincular todas as lojas presentes no merchant_scope do JWT — em uma única chamada.

POST /api/auth/link-all-merchants

Parâmetros

CampoTipoDescrição
domainstringDomínio do seu sistema cadastrado no painel.
authorizationCodestringCódigo de autorização retornado pelo iFood.
authorizationCodeVerifierstringVerificador obtido em generate-user-code.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/auth/link-all-merchants \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "authorizationCode": "codigo-do-ifood",
    "authorizationCodeVerifier": "verificador-da-etapa-1"
  }'

Resposta de sucesso

JSON
{
    "message": "Merchants linked successfully",
    "linked": ["uuid-loja-2", "uuid-loja-3"],
    "updated": ["uuid-loja-1"],
    "merchant_ids_in_token": ["uuid-loja-1", "uuid-loja-2", "uuid-loja-3"],
    "merchants": [
        { "id": 10, "merchant_id": "uuid-loja-1", "expires_at": "2026-09-30T05:00:00+00:00" },
        { "id": 11, "merchant_id": "uuid-loja-2", "expires_at": "2026-09-30T05:00:00+00:00" }
    ]
}
Quando usar: clientes com várias lojas na mesma conta iFood. Lojas já vinculadas entram em updated (tokens renovados); novas entram em linked.

Recebendo Webhooks

Após vincular um merchant, o iHub envia automaticamente todos os eventos de pedidos para a URL de webhook configurada no seu painel via POST.

Headers enviados

HeaderDescrição
Content-Typeapplication/json
Acceptapplication/json
X-iFood-Hub-SignatureSeu token secreto (compare para validar autenticidade).
Importante: Seu endpoint deve retornar HTTP 2xx para confirmar o recebimento. Caso contrário, o evento será marcado como falha.

Payload dos Webhooks

O corpo de cada webhook segue a estrutura de eventos do iFood:

CampoTipoDescrição
idstringID único do evento.
codestringCódigo resumido do evento (ex.: PLC, CFM, CAN).
fullCodestringCódigo completo (ex.: PLACED, CONFIRMED).
orderIdstringID do pedido no iFood.
merchantIdstringID da loja (merchant) no iFood.
createdAtstringTimestamp ISO 8601 do evento.

Pedido Novo (PLC)

Quando um novo pedido é criado (code: PLC), o iHub automaticamente busca os detalhes completos do pedido na API do iFood e os inclui no campo order_details — assim seu sistema não precisa fazer uma segunda requisição.

JSON
{
    "id": "evt-abc-123",
    "code": "PLC",
    "fullCode": "PLACED",
    "orderId": "order-xyz-789",
    "merchantId": "merchant-aaa-111",
    "createdAt": "2026-04-14T22:30:00Z",
    "order_details": {
        "id": "order-xyz-789",
        "type": "DELIVERY",
        "displayId": "1234",
        "orderType": "DELIVERY",
        "customer": {
            "name": "João da Silva",
            "phone": { "number": "11999999999" }
        },
        "deliveryAddress": {
            "streetName": "Rua Exemplo",
            "streetNumber": "100",
            "neighborhood": "Centro",
            "city": "São Paulo",
            "state": "SP",
            "postalCode": "01000-000",
            "complement": "Apto 42",
            "reference": "Próximo ao mercado"
        },
        "items": [
            {
                "id": "item-001",
                "name": "X-Burger",
                "quantity": 2,
                "unitPrice": 25.90,
                "totalPrice": 51.80,
                "subItems": [],
                "observations": "Sem cebola"
            }
        ],
        "payments": {
            "methods": [
                { "method": "CREDIT", "type": "ONLINE", "value": 51.80 }
            ]
        },
        "total": {
            "subTotal": 51.80,
            "deliveryFee": 5.00,
            "benefits": 0,
            "orderAmount": 56.80
        }
    }
}

Eventos de Status

Eventos subsequentes (confirmação, despacho, cancelamento, etc.) seguem a mesma estrutura, sem o campo order_details:

CódigofullCodeDescrição
PLCPLACEDNovo pedido criado.
CFMCONFIRMEDPedido confirmado pelo restaurante.
PSPPREPARATION_STARTEDPreparo iniciado.
DSPDISPATCHEDPedido despachado.
RTPREADY_TO_PICKUPPronto para retirada.
CANCANCELLEDPedido cancelado.
CONCONCLUDEDPedido concluído.
Exemplo — Confirmação
{
    "id": "evt-abc-456",
    "code": "CFM",
    "fullCode": "CONFIRMED",
    "orderId": "order-xyz-789",
    "merchantId": "merchant-aaa-111",
    "createdAt": "2026-04-14T22:32:00Z"
}

Executar Ação em Pedido

Use esta rota para confirmar, despachar, cancelar ou gerenciar pedidos no iFood.

POST /api/ifood/action

Parâmetros

CampoTipoObrigatórioDescrição
domainstringSimDomínio do seu sistema.
merchantIdstringSimID do merchant no iFood.
orderIdstringSimID do pedido.
actionstringSimAção a executar (ver tabela abaixo).
cancelCodestringSe cancelCódigo do motivo de cancelamento.
cancelReasonstringSe cancelDescrição do motivo.

Ações disponíveis

AçãoDescrição
confirmConfirma o recebimento do pedido.
startPreparationMarca o início do preparo.
readyToPickupMarca como pronto para retirada.
dispatchDespacha o pedido para entrega.
cancelCancela o pedido (requer cancelCode e cancelReason).

Exemplo — Confirmar pedido

cURL
curl -X POST https://ihub.arcn.com.br/api/ifood/action \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "action": "confirm"
  }'

Exemplo — Cancelar pedido

cURL
curl -X POST https://ihub.arcn.com.br/api/ifood/action \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "action": "cancel",
    "cancelCode": "501",
    "cancelReason": "Falta de ingredientes"
  }'

Resposta de sucesso

JSON
{
    "success": true,
    "message": "Order confirmed successfully"
}

Detalhes do Pedido

Busca os dados completos de um pedido diretamente na API do iFood. O cadastro é identificado pelo Authorization: Bearer; não é necessário informar o domínio na URL.

GET /api/orders/{merchantId}/{orderId}

Exemplo

cURL
curl https://ihub.arcn.com.br/api/orders/abcd-1234-efgh-5678/order-xyz-789 \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Retorna o JSON completo com dados do pedido (itens, cliente, endereço, pagamento).

Motivos de Cancelamento

Obtém a lista de motivos de cancelamento válidos para um pedido. O domínio é inferido do token.

GET /api/orders/{merchantId}/{orderId}/cancellation-reasons

Exemplo

cURL
curl https://ihub.arcn.com.br/api/orders/abcd-1234-efgh-5678/order-xyz-789/cancellation-reasons \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Retorna um array de objetos com cancelCodeId e description. Use o código na ação cancel.

Validar Código de Retirada

Valida o código de retirada apresentado pelo entregador no momento da coleta do pedido. Utilizado em pedidos com entrega própria (self-delivery) onde o entregador precisa confirmar a retirada.

POST /api/orders/validate-pickup-code

Parâmetros

CampoTipoObrigatórioDescrição
domainstringSimDomínio do seu sistema cadastrado no painel.
merchantIdstringSimID do merchant no iFood.
orderIdstringSimID do pedido no iFood.
codestringSimCódigo de retirada apresentado pelo entregador.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/orders/validate-pickup-code \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "code": "9999"
  }'

Resposta de sucesso

JSON
{
    "success": true
}

Códigos de erro

StatusDescrição
400Tipo de validação incorreto para este pedido.
404Pedido não encontrado ou endpoint indisponível (entrega iFood + recurso habilitado). Entrega própria valida localmente via pickupCode.
412Status de handshake inválido — o pedido deve ter sido aceito pelo entregador antes da validação.

Verificar Código de Entrega

Verifica o código de confirmação de entrega apresentado pelo destinatário no momento da entrega do pedido.

POST /api/orders/verify-delivery-code

Parâmetros

CampoTipoObrigatórioDescrição
domainstringSimDomínio do seu sistema cadastrado no painel.
merchantIdstringSimID do merchant no iFood.
orderIdstringSimID do pedido no iFood.
codestringSimCódigo de entrega apresentado pelo destinatário.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/orders/verify-delivery-code \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "code": "9999"
  }'

Resposta de sucesso

JSON
{
    "success": true
}

Códigos de erro

StatusDescrição
400Código de confirmação inválido (não confundir com código de retirada).
404Pedido não encontrado. Entrega própria: compare com customer.phone.localizer.

Módulo Merchant (Loja)

Endpoints para consultar e gerenciar lojas vinculadas ao iFood: listagem, detalhes, status operacional, pausas (interrupções), horários de funcionamento e QR code de check-in (Grocery).

Todas as rotas exigem Authorization: Bearer SEU_TOKEN_SECRETO. O {merchant_id} na URL deve ser o UUID da loja já vinculada ao seu cadastro no iHub (via Vincular Merchant).

Respostas: o iHub repassa o JSON da API Merchant do iFood quando possível. Erros comuns: 404 (loja não vinculada), 401 (token iFood expirado), 403 (sem permissão), 429 (rate limit — respeite o header Retry-After).

Listar lojas

Lista todas as lojas associadas ao token OAuth do merchant informado (paginação opcional).

GET /api/merchants/{merchantId}/list

Query (opcional)

CampoTipoDescrição
pageintegerPágina (mín. 1).
sizeintegerItens por página (máx. 100).

Exemplo

cURL
curl "https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/list?page=1&size=50" \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Detalhes da loja

Retorna informações completas de uma loja específica (nome, endereço, categorias, etc.).

GET /api/merchants/{merchantId}

Exemplo

cURL
curl https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678 \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Status da loja

Consulta o status operacional geral da loja (aberta, fechada, pausada, etc.).

GET /api/merchants/{merchantId}/status

Exemplo

cURL
curl https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/status \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Status por operação

Status filtrado por tipo de operação da loja.

GET /api/merchants/{merchantId}/status/{operation}

Parâmetros de URL

CampoValoresDescrição
operationDELIVERY, TAKEOUT, INDOORTipo de operação (maiúsculas).

Exemplo

cURL
curl https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/status/DELIVERY \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Erros

StatusDescrição
400operation inválida (use DELIVERY, TAKEOUT ou INDOOR).

Listar pausas (interrupções)

Lista interrupções temporárias ativas da loja (pausas programadas ou em andamento).

GET /api/merchants/{merchantId}/interruptions

Exemplo

cURL
curl https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/interruptions \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Criar pausa

Cria uma interrupção temporária de operação (ex.: pausa para manutenção ou falta de equipe).

POST /api/merchants/{merchantId}/interruptions

Body (JSON)

CampoTipoObrigatórioDescrição
descriptionstringSimMotivo da pausa (máx. 255 caracteres).
startdateSimInício da pausa (ISO 8601).
enddateSimFim da pausa (deve ser posterior a start).

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/interruptions \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Pausa para manutenção",
    "start": "2026-05-20T14:00:00Z",
    "end": "2026-05-20T16:00:00Z"
  }'

Resposta de sucesso

Status 201 Created com o objeto da interrupção criada (inclui id para remoção posterior).

Erros

StatusDescrição
409Sobreposição com outra interrupção já existente.

Remover pausa

Remove uma interrupção ativa pelo ID retornado na criação ou listagem.

DELETE /api/merchants/{merchantId}/interruptions/{interruptionId}

Exemplo

cURL
curl -X DELETE https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/interruptions/interruption-uuid-aqui \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO"

Resposta de sucesso

Status 204 No Content (corpo vazio).

Horários de funcionamento

Consulta os turnos de funcionamento cadastrados no iFood para a loja.

GET /api/merchants/{merchantId}/opening-hours

Exemplo

cURL
curl https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/opening-hours \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Atualizar horários

Substitui todos os turnos de funcionamento da loja. Dias da semana não enviados no array shifts ficam fechados.

Atenção: esta operação é destrutiva — envie a grade completa de turnos que deseja manter, não apenas o dia alterado.
PUT /api/merchants/{merchantId}/opening-hours

Body (JSON)

CampoTipoObrigatórioDescrição
storeIduuidSimID da loja no iFood (obtido em detalhes da loja).
shiftsarraySimLista de turnos (mín. 1).
shifts[].dayOfWeekstringSimMONDAY … SUNDAY
shifts[].startstringSimHorário de abertura (H:i:s, ex.: 08:00:00).
shifts[].durationintegerSimDuração do turno em minutos.

Exemplo

cURL
curl -X PUT https://ihub.arcn.com.br/api/merchants/abcd-1234-efgh-5678/opening-hours \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "c54bb20a-bce0-4e38-bd4a-fe5f0a7b6b5a",
    "shifts": [
      {
        "dayOfWeek": "MONDAY",
        "start": "08:00:00",
        "duration": 720
      },
      {
        "dayOfWeek": "TUESDAY",
        "start": "08:00:00",
        "duration": 720
      }
    ]
  }'

Resposta de sucesso

Status 201 Created com os turnos criados/atualizados.

Erros

StatusDescrição
400Validação (turnos sobrepostos, formato inválido, etc.).
409Conflito na atualização.

QR Code de check-in

Gera um PDF com QR codes de check-in para entregadores. Destinado a lojas do tipo Grocery. Aceita de 1 a 20 lojas por requisição.

POST /api/merchants/checkin-qrcode

Body (JSON)

CampoTipoObrigatórioDescrição
merchantIdsuuid[]SimIDs das lojas (1 a 20). O token é resolvido pela primeira loja da lista.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/merchants/checkin-qrcode \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantIds": [
      "abcd-1234-efgh-5678",
      "9876-5432-1111-2222-333344445555"
    ]
  }' \
  --output checkin-qrcode.pdf

Resposta de sucesso

Status 200 com corpo binário application/pdf (arquivo para download/impressão).

Módulo Shipping (Entrega)

Use este módulo para pedir um entregador do iFood (Easy Delivery) para um pedido feito fora da plataforma iFood — WhatsApp, telefone, PDV, aplicativo próprio, etc. É indicado para lojas com frota própria que precisam de reforço em picos de demanda.

Diferença para os demais módulos: este fluxo cria um pedido no iFood a partir de um pedido que já existe no seu sistema. Uma vez registrado, ele passa a se comportar como um pedido normal do iFood: os eventos de status (despacho, entrega, cancelamento) chegam pelo seu webhook normalmente. O cancelamento tem rota própria — veja Cancelamento — não use POST /api/ifood/action com action: "cancel" aqui, os motivos/códigos são diferentes.
Dois cenários no mesmo módulo: Shipping cobre tanto pedidos fora da plataforma (esta seção — a loja cria o pedido no iFood) quanto pedidos nativos do iFood onde a loja usa entrega própria mas quer suporte pontual da frota do iFood (veja Pedido nativo do iFood, mais abaixo). São rotas e endpoints diferentes — não misture os dois fluxos.

Todas as rotas exigem Authorization: Bearer SEU_TOKEN_SECRETO. O {merchant_id} na URL deve ser o UUID da loja já vinculada ao seu cadastro no iHub (via Vincular Merchant).

Schema confirmado: os campos abaixo foram validados contra a documentação oficial do iFood Developer (developer.ifood.com.br/pt-BR/docs/food/guides/modules/shipping/intro) e testes reais em produção — não são mais estimativa.

Disponibilidade de entrega

Consulta se há cobertura de entrega para um endereço (área atendida, frota disponível) antes de registrar o pedido. Passo obrigatório — o id retornado aqui é o quoteId exigido no passo seguinte, e a cotação expira (expirationAt).

GET /api/shipping/{merchantId}/delivery-availability

Query

CampoTipoObrigatórioDescrição
latitudenumberSimLatitude do endereço de entrega.
longitudenumberSimLongitude do endereço de entrega.

Exemplo

cURL
curl "https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/delivery-availability?latitude=-23.55052&longitude=-46.633308" \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "quote": { "grossValue": 12.90, "discount": 0, "netValue": 12.90 },
    "deliveryTime": { "min": 20, "max": 40 },
    "distance": 1850,
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "expirationAt": "2026-08-27T19:07:34.723Z",
    "createdAt": "2026-08-27T19:07:34.723Z",
    "hasPaymentMethods": true,
    "paymentMethods": [
        { "id": "...", "brand": "string", "liability": "string", "paymentType": "string", "method": "string" }
    ]
}

Guarde o id (é o quoteId) e o quote.netValue — ambos são usados no passo de solicitar o entregador, abaixo.

Erros

StatusDescrição
400Endereço fora da área de cobertura ou sem entregador parceiro disponível no momento (ex.: code: "DeliveryDistanceTooHigh" — pode ocorrer mesmo com distância real pequena, se a loja não tiver cobertura de parceiros habilitada na região).
404Merchant não vinculado ao seu cadastro no iHub.

Solicitar entregador

Registra no iFood um pedido feito fora da plataforma e solicita a alocação automática de um entregador, a partir de uma cotação obtida em Disponibilidade de entrega. Em caso de sucesso, o iFood retorna o id do pedido gerado e uma trackingUrl para o cliente acompanhar a entrega.

POST /api/shipping/{merchantId}/orders

Body (JSON)

CampoTipoObrigatórioDescrição
customer.namestringSimNome do cliente que vai receber o pedido.
customer.phoneobjectSimcountryCode, areaCode e number — todos exigidos separadamente pelo iFood.
delivery.quoteIdstringRecomendadoO id retornado pela consulta de disponibilidade.
delivery.merchantFeenumberRecomendadoValor da entrega — use o quote.netValue retornado na disponibilidade.
delivery.deliveryAddressobjectSimpostalCode, streetNumber, streetName, complement, neighborhood, city, state, country, reference.
delivery.deliveryAddress.coordinatesobjectSim{ "latitude": 0, "longitude": 0 } — objeto aninhado, não campos soltos.
itemsarraySimCada item exige id (UUID válido — gere um sintético se não tiver um real), name, quantity, unitPrice, price, optionsPrice, totalPrice (decimais em reais, não centavos).
paymentsobjectNão, se já pagoOmita o campo inteiro se o pedido já foi pago no seu sistema (o iFood não deve processar pagamento). Se pendente: { "methods": [{ "method": "CASH"|"CREDIT"|"DEBIT", "type": "OFFLINE", "value": 0, "cash": {"changeFor": 0} }] } — cash.changeFor é obrigatório para CASH, card.brand é obrigatório para CREDIT/DEBIT.
displayIdstringNãoCódigo do pedido no seu sistema, exibido pro entregador. Máximo 4 caracteres.

Exemplo de requisição

cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": {
        "name": "João da Silva",
        "phone": { "countryCode": "55", "areaCode": "11", "number": "999999999" }
    },
    "delivery": {
        "quoteId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "merchantFee": 12.90,
        "deliveryAddress": {
            "postalCode": "01000-000",
            "streetNumber": "100",
            "streetName": "Rua Exemplo",
            "complement": "Apto 42",
            "neighborhood": "Centro",
            "city": "São Paulo",
            "state": "SP",
            "country": "BR",
            "reference": "Próximo ao mercado",
            "coordinates": { "latitude": -23.55052, "longitude": -46.633308 }
        }
    },
    "items": [
        { "id": "1", "name": "X-Burger", "quantity": 2, "unitPrice": 25.90, "price": 51.80, "optionsPrice": 0, "totalPrice": 51.80 }
    ],
    "payments": {
        "methods": [
            { "method": "CASH", "type": "OFFLINE", "value": 51.80 }
        ]
    },
    "displayId": "1234"
  }'

Resposta de sucesso

JSON (202)
{
    "id": "order-uuid-gerado-pelo-ifood",
    "trackingUrl": "https://tracking.ifood.com.br/order-uuid-gerado-pelo-ifood"
}

Guarde o id retornado — ele é o mesmo orderId que vai aparecer nos eventos enviados ao seu webhook (Eventos de Status) e que deve ser usado em ações de pedido (cancelamento, etc.).

Erros

StatusDescrição
400Entrega não viável (endereço fora de cobertura, sem entregador disponível, etc.).
404Merchant não vinculado ao seu cadastro no iHub.
401Token OAuth do merchant expirado/inválido.

Cancelamento

Cancelamento de pedidos do módulo Shipping é em duas etapas: consultar os motivos válidos e enviar o cancelamento com o código escolhido. Não use a ação cancel de POST /api/ifood/action para pedidos registrados via Shipping — os motivos/códigos são diferentes dos do módulo Order.

GET /api/shipping/{merchantId}/orders/{orderId}/cancellation-reasons

Exemplo

cURL
curl https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/cancellation-reasons \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON
[
    { "cancelCodeId": "817", "description": "O cliente cancelou o pedido" },
    { "cancelCodeId": "818", "description": "Impossível preparar o pedido" }
]

Status 204 (corpo vazio) significa que o pedido não pode mais ser cancelado (já entregue, já cancelado, etc.).

POST /api/shipping/{merchantId}/orders/{orderId}/cancel

Body (JSON)

CampoTipoObrigatórioDescrição
reasonstringSimTexto explicativo (para logs).
cancellationCodestringSimO cancelCodeId obtido em cancellation-reasons (ex.: "817"). String — a doc oficial do iFood diz "integer", mas o erro real da API confirma que é string (expected=string, got=number).

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/cancel \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Cliente solicitou cancelamento via chat",
    "cancellationCode": "817"
  }'

Resposta de sucesso

JSON
{ "success": true }

O cancelamento é assíncrono — acompanhe pelo webhook (CANCELLATION_REQUESTED → CANCELLED, ou CANCELLATION_REQUEST_FAILED se der errado, ex.: entregador já saiu pra entrega).

Safe Delivery Score

Nível de confiança calculado automaticamente pelo iFood para cada entrega, com base em validação de telefone e confirmação de endereço do cliente.

GET /api/shipping/{merchantId}/orders/{orderId}/safe-delivery

Exemplo

cURL
curl https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/safe-delivery \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON
{
    "score": "VERY_HIGH",
    "rules": {
        "customer_phone_valid": true,
        "customer_phone_is_fixed": false,
        "customer_address_confirmed": true,
        "customer_address_change_requested": false,
        "merchant_address_change_approved": false,
        "merchant_address_change_denied": false
    }
}

score: LOW, MODERATE, HIGH ou VERY_HIGH. Consulte perto do horário de entrega, não só na criação — o score muda conforme o cliente confirma/altera o endereço.

Troca de endereço

Quando o cliente solicita mudar o endereço de entrega pela página de rastreamento do iFood (trackingUrl), você recebe o evento DELIVERY_ADDRESS_CHANGE_REQUESTED no seu webhook. Use estas rotas para aceitar ou recusar (máximo ~15 minutos para responder — depois disso o iFood recusa automaticamente).

POST /api/shipping/{merchantId}/orders/{orderId}/accept-address-change
cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/accept-address-change \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO"

Gera o evento DELIVERY_ADDRESS_CHANGE_ACCEPTED. Se o novo endereço cair em outra região de cobertura, o iFood nega automaticamente com DELIVERY_ADDRESS_CHANGE_DENIED (metadata.action = "region-mismatch").

POST /api/shipping/{merchantId}/orders/{orderId}/deny-address-change
cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/deny-address-change \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO"

Gera o evento DELIVERY_ADDRESS_CHANGE_DENIED.

Não documentado pelo iFood: não encontrei um exemplo oficial do payload do evento DELIVERY_ADDRESS_CHANGE_REQUESTED no webhook. O iHub repassa o evento como recebido, sem transformação — trate o payload de forma defensiva no seu sistema até confirmar o formato exato em produção.

Loja propõe uma correção de endereço

Direção oposta da anterior: a loja propõe um novo endereço (ex.: o capturado estava errado/incompleto) — diferente de aceitar/recusar uma solicitação do cliente. O cliente recebe a proposta na página de rastreamento do iFood e tem até ~15 minutos para confirmar.

POST /api/shipping/{merchantId}/orders/{orderId}/request-address-change

Body (JSON)

CampoTipoObrigatórioDescrição
streetName / streetNumber / complement / neighborhood / city / state / country / referencestringNãoCampos do novo endereço proposto.
coordinatesobjectSim{ "latitude": 0, "longitude": 0 } — coordenadas do novo endereço.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-xyz-789/request-address-change \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "streetName": "Rua Corrigida",
    "streetNumber": "42",
    "neighborhood": "Centro",
    "city": "São Paulo",
    "state": "SP",
    "country": "BR",
    "coordinates": { "latitude": -23.55052, "longitude": -46.633308 }
  }'

Acompanhe a resolução pelos mesmos eventos de webhook: DELIVERY_ADDRESS_CHANGE_ACCEPTED, DELIVERY_ADDRESS_CHANGE_DENIED ou DELIVERY_ADDRESS_CHANGE_USER_CONFIRMED.

Pedido nativo do iFood (entrega própria + suporte da frota)

Cenário diferente do restante deste módulo: aqui o pedido já existe nativamente no iFood (chegou pelo seu webhook normal, como qualquer pedido do módulo Order) e a loja usa entrega própria como modelo padrão de logística — mas quer, pontualmente, pedir um entregador da frota do iFood como reforço. O pedido não é criado por essas rotas (ele já existe) — só a solicitação/cancelamento do entregador passa por aqui.

Diferença chave: como o pedido já existe no iFood com todos os dados de cliente/itens/pagamento, o corpo das requisições é bem mais simples do que no fluxo "fora da plataforma" — só o orderId (já nativo) e, na solicitação, o quoteId.
GET /api/shipping/{merchantId}/orders/{orderId}/delivery-availability

Mesma resposta da disponibilidade por merchant (quote, deliveryTime, distance, id = quoteId, expirationAt), mas sem query params — o iFood já sabe o endereço porque o pedido já existe no lado dele.

cURL
curl https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-nativo-789/delivery-availability \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO"

Solicitar entregador (pedido nativo)

POST /api/shipping/{merchantId}/orders/{orderId}/request-driver

Body (JSON)

CampoTipoObrigatórioDescrição
quoteIdstringSimO id retornado pela disponibilidade acima.
cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-nativo-789/request-driver \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{ "quoteId": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }'

Resposta 202 Accepted, sem corpo (diferente do fluxo fora da plataforma, que retorna trackingUrl — pedidos nativos já têm rastreamento nativo no app do iFood pro cliente).

Cancelar solicitação de entregador (pedido nativo)

POST /api/shipping/{merchantId}/orders/{orderId}/cancel-request-driver

Cancela só a solicitação de entregador — o pedido em si continua existindo no iFood. Sem corpo na requisição. Não use POST .../cancel (a rota da seção Cancelamento) aqui: ela é exclusiva do fluxo fora da plataforma e retorna erro RequestCancelSalesChannelInvalid se usada num pedido nativo.

cURL
curl -X POST https://ihub.arcn.com.br/api/shipping/abcd-1234-efgh-5678/orders/order-nativo-789/cancel-request-driver \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO"

Resposta de sucesso

JSON
{ "success": true }

Nas requisições POST /api/ifood/action abaixo, envie sempre Authorization: Bearer SEU_TOKEN_SECRETO (igual às demais rotas).

Aceitar Disputa

Aceita uma disputa aberta pelo cliente no iFood.

POST /api/ifood/action
JSON
{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "action": "acceptDispute",
    "disputeId": "dispute-id-aqui",
    "reason": "Concordo com o cliente",
    "detailReason": "Produto realmente estava errado"
}

Rejeitar Disputa

Rejeita uma disputa. disputeId e reason são obrigatórios.

POST /api/ifood/action
JSON
{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "action": "rejectDispute",
    "disputeId": "dispute-id-aqui",
    "reason": "Pedido foi entregue corretamente"
}

Propor Reembolso

Propõe um reembolso parcial em uma disputa. disputeId, alternativeId e refundAmount são obrigatórios.

POST /api/ifood/action
JSON
{
    "domain": "app.meudelivery.com.br",
    "merchantId": "abcd-1234-efgh-5678",
    "orderId": "order-xyz-789",
    "action": "proposeRefund",
    "disputeId": "dispute-id-aqui",
    "alternativeId": "alternative-id",
    "refundAmount": "15.50"
}

Módulo Analytics (KPIs)

Consulta agregada de indicadores de performance de pedidos por loja — GMV, ticket médio, distribuição por canal de venda, status, forma de pagamento e modelo logístico. Não retorna dados de pedidos individuais, só métricas agregadas sobre um período.

Dados D-1: o iFood processa um snapshot dos pedidos até a meia-noite (fuso da loja) e disponibiliza os dados agregados até às 9h do dia seguinte. Não é tempo real — um pedido pode inclusive mudar de status (ex.: concluído → cancelado) em até 15 dias após a conclusão, então os números de um período recente podem variar entre consultas.
Formato da resposta depende do que você pede: o corpo aceita quatro tipos de agregação (metrics, terms, groupBy, dateIntervals) — pelo menos um é obrigatório. Recomendamos groupBy (combinado com metrics) em vez de terms: no ambiente de homologação (x-request-homologation: true), o iFood sempre retorna um payload fixo de teste no formato groupBy, então usar esse formato garante que sua consulta funcione igual em teste e produção.
POST /api/analytics/{merchantId}/orders/kpis

Body (JSON)

CampoTipoObrigatórioDescrição
filter.referenceDateobjectSim{ "gte": "2026-01-01 00:00:00", "lte": "2026-01-31 23:59:59" } — período consultado, baseado na data de criação do pedido.
filter.orderStatus / salesChannels / deliveredBy / categoriesarrayNãoFiltros adicionais (ex.: ["CONCLUDED"]).
agg.metricsobjectAo menos um de agg.*Métricas numéricas: { "gmv": ["sum"], "gmvWithoutDelivery": ["sum"] } — funções aceitas: sum, avg, min, max.
agg.groupBy.fieldsarrayAo menos um de agg.*Campos pra quebrar o resultado em linhas: orderStatus, salesChannel, paymentMethod, deliveredBy, dayOfWeek, category, orderCreationHour, etc.
agg.termsobjectAo menos um de agg.*Contagem por valor de campo: { "orderStatus": ["count"] } — produz um único objeto agregado (sem quebra em linhas). Ver aviso acima sobre homologação.
pageintegerSim1 a 1000.
sizeintegerSim1 a 10000.
sort.groupByKeystringNãoasc ou desc — só tem efeito com agg.groupBy.

Exemplo de requisição

cURL
curl -X POST https://ihub.arcn.com.br/api/analytics/abcd-1234-efgh-5678/orders/kpis \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
        "referenceDate": { "gte": "2026-01-01 00:00:00", "lte": "2026-01-31 23:59:59" }
    },
    "agg": {
        "metrics": { "gmv": ["sum"], "gmvWithoutDelivery": ["sum"] },
        "groupBy": { "fields": ["orderStatus", "salesChannel", "paymentMethod", "deliveredBy"] }
    },
    "page": 1,
    "size": 100
  }'

Resposta de sucesso

JSON (200)
{
    "currentPage": 1,
    "size": 100,
    "totalItems": 12,
    "totalPages": 1,
    "data": [
        {
            "groupByKey": {
                "value": { "orderStatus": "CONCLUDED", "salesChannel": "IFOOD", "paymentMethod": "PIX", "deliveredBy": "MERCHANT_DELIVERY" },
                "count": 159
            },
            "gmv": { "sum": 15230.93 },
            "gmvWithoutDelivery": { "sum": 15210.95 }
        }
    ],
    "_meta": {
        "queriedAt": "2026-09-10T14:32:07+00:00",
        "clientId": "b685a0d6-...",
        "homologation": false
    }
}

Cada linha de data é uma combinação única dos campos de groupBy, com count (quantidade de pedidos naquela combinação) e as métricas agregadas daquela combinação. Some as linhas que te interessam pra chegar num total (ex.: GMV total = soma de gmv.sum de todas as linhas; pedidos concluídos = soma de count onde orderStatus == "CONCLUDED"). O campo _meta é acrescentado pelo iHub (não existe no schema do iFood) — útil pra exibir data/hora da consulta e o client_id do app na sua própria UI.

Homologação

Envie o header X-Request-Homologation: true pra consultar o ambiente de teste do iFood — a resposta vem num formato fixo (sempre groupBy por dayOfWeek/orderStatus/deliveredBy/paymentMethod/salesChannel), útil pra validar sua integração sem depender de dados reais. Nesse modo, o iHub não usa o cache de 30 minutos descrito abaixo — cada chamada é sempre fresca.

Erros

StatusDescrição
400Validação falhou — filter.referenceDate ausente, nenhuma agregação em agg, ou campo/função inválidos.
404Merchant não vinculado ao seu cadastro no iHub, ou token OAuth expirado/inválido.
429Limite de 500 requisições/minuto do iFood excedido. O iHub já tenta novamente automaticamente em erros transitórios (5xx/429 com espera curta).
500Erro interno do iFood.
Cache: como os dados são D-1 (não mudam durante o dia), o iHub cacheia respostas de sucesso por 30 minutos (chave = merchant + corpo exato da consulta) pra reduzir chamadas repetidas ao iFood. Mude qualquer parâmetro do corpo (período, agregação) pra forçar uma consulta fresca.

Módulo Review (Avaliações)

Consulta e resposta das avaliações que os clientes deixam depois de um pedido — nota de 1 a 5, comentário, pesquisa de satisfação estruturada e o pedido vinculado. A loja só lê e responde; quem avalia é sempre o cliente.

Todas as rotas exigem Authorization: Bearer SEU_TOKEN_SECRETO. O {merchantId} na URL deve ser o UUID da loja já vinculada ao seu cadastro no iHub (via Vincular Merchant).

Política de Avaliações: se sua aplicação exibe ou responde avaliações, o iFood exige um link visível para a Política de Avaliações na sua interface — é um pré-requisito de homologação do módulo.

Listar avaliações

Lista as avaliações da loja, paginado, com filtro opcional por período e por pedido.

GET /api/review/{merchantId}/reviews

Query string

CampoTipoDescrição
pageintegerPágina (padrão 1).
pageSizeintegerItens por página — máximo real 50. Acima disso o iFood responde 400 (o iHub não impõe um limite mais baixo, pra deixar esse erro real passar).
dateFrom / dateTostringFiltro por período de criação da avaliação (ISO 8601, ex.: 2026-09-01T00:00:00Z).
orderIdstringFiltra pela avaliação de um pedido específico.
customerNamestringFiltra pelo nome do cliente.

Exemplo

cURL
curl "https://ihub.arcn.com.br/api/review/abcd-1234-efgh-5678/reviews?page=1&pageSize=10&dateFrom=2026-09-01T00:00:00Z&dateTo=2026-09-30T23:59:59Z" \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "page": 1,
    "size": 10,
    "reviews": [
        {
            "id": "a3e01caa-4f89-4ec0-89c5-f18044e86fb8",
            "score": 5,
            "hasScoreChanged": false,
            "status": "NOT_REPLIED",
            "comment": "Muito bom, chegou rápido!",
            "replies": [],
            "createdAt": "2026-09-24T23:50:14.333752Z",
            "order": { "id": "58db79bb-c11c-4b99-a174-8b92cb8fb781", "shortId": "1086", "createdAt": "2026-09-24T23:42:11.391Z" },
            "merchant": { "id": "abcd-1234-efgh-5678", "name": "Nome da Loja" },
            "surveyId": "7a77ef49-82d4-4671-92fa-bed7322d6069",
            "version": "v2",
            "visibility": "PRIVATE"
        }
    ]
}
A resposta fica em "replies", não em "answer": a resposta da loja (quando existe) aparece em replies[] — um array, com from: "MERCHANT" ou "CUSTOMER" — não num campo answer único. Além disso, o campo status pode continuar NOT_REPLIED por um tempo mesmo depois de responder (o iFood atualiza esse campo de forma assíncrona, com atraso). Pra saber com certeza se uma avaliação já tem resposta, confira replies.length > 0 — não confie só em status.

Erros

StatusDescrição
400pageSize acima de 50.
404Merchant não vinculado ao seu cadastro no iHub, ou token OAuth expirado/inválido.
429Rate limit excedido.

Detalhes da avaliação

Detalhes completos de uma avaliação: pedido vinculado, perguntas e respostas da pesquisa de satisfação, e o histórico de respostas da loja.

GET /api/review/{merchantId}/reviews/{reviewId}

Exemplo

cURL
curl https://ihub.arcn.com.br/api/review/abcd-1234-efgh-5678/reviews/a3e01caa-4f89-4ec0-89c5-f18044e86fb8 \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "id": "a3e01caa-4f89-4ec0-89c5-f18044e86fb8",
    "score": 5,
    "status": "NOT_REPLIED",
    "comment": "Muito bom, chegou rápido!",
    "customerName": "Maria",
    "replies": [
        { "createdAt": "2026-09-27T21:22:36.862495", "text": "Que bom que gostou!", "from": "MERCHANT" }
    ],
    "createdAt": "2026-09-24T23:50:14.333752Z",
    "order": { "id": "58db79bb-c11c-4b99-a174-8b92cb8fb781", "shortId": "1086", "createdAt": "2026-09-24T23:42:11.391Z" },
    "merchant": { "id": "abcd-1234-efgh-5678", "name": "Nome da Loja" },
    "surveyId": "7a77ef49-82d4-4671-92fa-bed7322d6069",
    "version": "v2",
    "visibility": "PRIVATE",
    "questions": [
        {
            "id": "bf433963-657f-410e-ae8e-a3e8ab913604",
            "type": "TEXT",
            "title": "Deixar comentário",
            "answers": [{ "id": "cc09284b-9c55-445b-a2b7-f00462600d6c", "title": "Muito bom, chegou rápido!" }]
        },
        {
            "id": "57252d92-e048-475d-a68e-f9619c6c2c95",
            "type": "CHOICE_MULTIPLE",
            "title": "",
            "answers": [{ "id": "a40b7179-4421-4708-879f-a0323d626156", "title": "Temperatura certa" }]
        },
        {
            "id": "a6baadc5-e3d2-4ba3-8be6-2fded2c30409",
            "type": "RATING",
            "title": "O que você achou do pedido?",
            "answers": [{ "id": "0faa9d56-1723-4753-ab4f-ab340d9ff35d", "title": "5" }]
        }
    ]
}

Erros

StatusDescrição
400reviewId não é um UUID válido.
404Avaliação inexistente, merchant não vinculado, ou token inválido.

Responder avaliação

Responde uma avaliação. O iFood não tem endpoint separado de edição — reenviar pra esse mesmo endpoint substitui o texto de uma resposta já existente, em vez de criar uma segunda.

POST /api/review/{merchantId}/reviews/{reviewId}/answers

Body (JSON)

CampoTipoObrigatórioDescrição
textstringSimEntre 10 e 300 caracteres.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/review/abcd-1234-efgh-5678/reviews/a3e01caa-4f89-4ec0-89c5-f18044e86fb8/answers \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Obrigado pelo feedback! Vamos melhorar o tempo de entrega." }'

Resposta de sucesso

JSON (201)
{
    "createdAt": "2026-09-27T21:28:40.376792935Z",
    "reviewId": "a3e01caa-4f89-4ec0-89c5-f18044e86fb8",
    "text": "Obrigado pelo feedback! Vamos melhorar o tempo de entrega."
}

Erros

StatusDescrição
400text com menos de 10 ou mais de 300 caracteres.
404Avaliação inexistente.

Resumo

Nota média e contagem de avaliações da loja.

GET /api/review/{merchantId}/summary

Exemplo

cURL
curl https://ihub.arcn.com.br/api/review/abcd-1234-efgh-5678/summary \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "totalReviewsCount": 142,
    "validReviewsCount": 53,
    "score": 4.3
}
Resumo é assíncrono: o iFood calcula esse resumo em segundo plano — uma avaliação recém-criada pode já aparecer na listagem, mas ainda não ter entrado nessa conta. Quando a loja ainda não tem avaliações suficientes pra calcular um resumo, o iFood responde 404 em vez de um objeto zerado; o iHub normaliza isso automaticamente pra {"totalReviewsCount":0,"validReviewsCount":0,"score":0}, então você nunca vê esse 404 — trate como "loja ainda sem avaliações".

Módulo Catalog (Cardápio)

Gerenciamento completo do cardápio da loja: catálogos, categorias, itens (simples, com complementos, combos e pizzas), preço e disponibilidade — inclusive em lote —, agendamento de horário, inventário e upload de imagem.

Todas as rotas exigem Authorization: Bearer SEU_TOKEN_SECRETO. O {merchantId} na URL deve ser o UUID da loja já vinculada ao seu cadastro no iHub (via Vincular Merchant).

Modelo de dados: um catálogo agrupa categorias, que agrupam itens. Cada item aponta pra um product (nome, descrição, foto) e pode ter optionGroups (grupos de complementos), cada um com suas options (os complementos em si, que também apontam pra um product próprio). Preço e disponibilidade existem em dois lugares independentes — a raiz do item/opção e contextModifiers por canal (DEFAULT = Entrega, WHITELABEL = Cardápio Digital, INDOOR = Consumo no Local) — ver aviso na seção de preço de item.
Produtos "industrializados": se você informar um ean (código de barras) que bate com um produto do catálogo central do iFood (ex.: uma bebida de marca conhecida), o iFood assume nome, foto, descrição e peso desse produto automaticamente e o marca como industrialized: true na resposta. A partir daí, esse product não pode mais ser alterado por você — nem nome, nem foto, nem complementos — mesmo reenviando os mesmos dados sem nenhuma mudança. Tentar resulta em 400 "Not allowed to update product: industrialized product, different owner". Não informe ean se precisar manter controle total sobre nome/foto/complementos do item.

Listar catálogos

Lista os catálogos da loja — normalmente só um, mas lojas com múltiplos canais configurados de forma independente (multisetup) podem ter mais de um.

GET /api/catalog/{merchantId}/catalogs

Exemplo

cURL
curl https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/catalogs \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
[
    {
        "catalogId": "37e1a777-f6ae-43c9-a0e3-107fdf1a0069",
        "context": ["DEFAULT"],
        "status": "AVAILABLE"
    }
]

Os valores reais de context são DEFAULT (Entrega), WHITELABEL (Cardápio Digital) e INDOOR (Consumo no Local) — não confunda com "DELIVERY", que não é um valor válido apesar de parecer intuitivo.

Listar categorias

Lista as categorias de um catálogo.

GET /api/catalog/{merchantId}/catalogs/{catalogId}/categories

Query string

CampoTipoDescrição
include_itemsbooleanSe true, tenta incluir os itens de cada categoria na mesma resposta.

Exemplo

cURL
curl https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/catalogs/37e1a777-f6ae-43c9-a0e3-107fdf1a0069/categories \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
[
    { "id": "7680bafc-dc71-4614-a658-a9eb47ffc94b", "name": "Pizzas", "status": "AVAILABLE", "sequence": 1, "index": 1, "template": "PIZZA" },
    { "id": "0f652e28-ed9e-4b97-9c9b-7ca828bd6913", "name": "Sobremesas", "status": "AVAILABLE", "sequence": 2, "index": 2, "template": "DEFAULT" }
]

Criar categoria

Cria uma categoria dentro de um catálogo.

POST /api/catalog/{merchantId}/catalogs/{catalogId}/categories

Body (JSON)

CampoTipoObrigatórioDescrição
namestringSimNome da categoria.
statusstringSimAVAILABLE ou UNAVAILABLE.
templatestringNãoDEFAULT ou PIZZA. O iHub já manda DEFAULT se você omitir.
"template" é obrigatório de verdade: a documentação oficial trata como opcional, mas a API rejeita com "template must be one of the following values: DEFAULT, PIZZA" se ele não vier. O iHub já aplica DEFAULT como padrão pra você não precisar lembrar, mas se for cadastrar uma categoria de pizza, envie template: "PIZZA" explicitamente.

Exemplo

cURL
curl -X POST https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/catalogs/37e1a777-f6ae-43c9-a0e3-107fdf1a0069/categories \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Bebidas", "status": "AVAILABLE", "template": "DEFAULT" }'

Resposta de sucesso

Status 201 Created com o objeto da categoria criada (inclui id, usado nos próximos passos).

Itens de uma categoria

Lista os itens cadastrados dentro de uma categoria.

GET /api/catalog/{merchantId}/categories/{categoryId}/items

Exemplo

cURL
curl https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/categories/7680bafc-dc71-4614-a658-a9eb47ffc94b/items \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "categoryId": "7680bafc-dc71-4614-a658-a9eb47ffc94b",
    "items": [
        {
            "id": "eb09eba5-bdc8-4cb3-aacc-77a31017a9eb",
            "type": "DEFAULT",
            "categoryId": "7680bafc-dc71-4614-a658-a9eb47ffc94b",
            "status": "AVAILABLE",
            "price": { "value": 42.9 },
            "productId": "9cd01070-1747-460c-87be-89ba4db9a96e",
            "externalCode": "PIZZA_MUSSARELA"
        }
    ]
}

Criar ou atualizar item

Endpoint único pra criar e atualizar qualquer tipo de item: simples, com complementos, combo ou pizza — o que muda é a estrutura do payload, não a rota. É um upsert completo: reenviar o mesmo item.id com campos diferentes substitui o item inteiro (não faz merge parcial — ver aviso sobre shifts abaixo).

PUT /api/catalog/{merchantId}/items

Body (JSON)

CampoTipoObrigatórioDescrição
item.idstringSimUUID gerado por você — usado como identificador estável em atualizações futuras.
item.productIdstringSimPrecisa apontar pro id de um objeto em products. A doc oficial não deixa isso claro; omitir gera "item.productId must be a UUID".
item.categoryIdstringDependeObrigatório pra itens DEFAULT/COMBO_V2. Omita em itens PIZZA — ver aviso na seção de Pizza.
item.statusstringSimAVAILABLE ou UNAVAILABLE.
item.typestringNãoDEFAULT (padrão), PIZZA ou COMBO_V2.
item.price.valuenumberNãoPreço do item (não usado em COMBO_V2/PIZZA, cujo preço vem da combinação de opções escolhidas).
item.externalCodestringNãoCódigo do seu PDV — fica salvo, mas não é aceito como chave nos endpoints de PATCH de preço/status (eles exigem o itemId do iFood).
item.shiftsarrayNãoAgendamento de disponibilidade por dia/horário — ver seção própria.
productsarrayNãoUm objeto por produto referenciado (o item principal e, se houver, cada complemento/opção). Campos: id, name, description, imagePath, ean, weight.
optionGroupsarrayNãoGrupos de complementos — ver seção própria.
optionsarrayNãoAs opções (complementos) em si.

Exemplo — item simples

cURL
curl -X PUT https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/items \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '{
    "item": {
        "id": "SEU-UUID-1",
        "type": "DEFAULT",
        "categoryId": "7680bafc-dc71-4614-a658-a9eb47ffc94b",
        "productId": "SEU-UUID-2",
        "status": "AVAILABLE",
        "price": { "value": 25.90 },
        "externalCode": "SKU-001"
    },
    "products": [
        { "id": "SEU-UUID-2", "name": "X-Burger", "description": "Pão, carne, queijo e salada" }
    ],
    "optionGroups": [],
    "options": []
  }'

Resposta de sucesso

Status 200 OK com o item completo já persistido (inclui contextModifiers gerados automaticamente pro contexto DEFAULT).

Validações reais aplicadas pelo iFood

CampoRegra confirmada
products[].nameAté 150 caracteres (não 100, como uma leitura apressada da doc sugere).
products[].weight.unitSó aceita kg, g, l ou ml — qualquer outro valor (ex.: "unidades") retorna 400. Se o item for vendido por unidade, simplesmente omita weight.

Complementos (grupos de opção)

Um grupo de complementos (optionGroups[]) e suas opções (options[]) são criados no mesmo payload de PUT /items, referenciados a partir do product do item principal.

JSON — trecho do body
{
    "item": { "id": "ITEM-ID", "type": "DEFAULT", "categoryId": "CAT-ID", "productId": "PROD-ID", "status": "AVAILABLE", "price": { "value": 25.90 } },
    "products": [
        { "id": "PROD-ID", "name": "X-Burger", "optionGroups": [ { "id": "GRUPO-ID", "min": 0, "max": 3 } ] },
        { "id": "PROD-QUEIJO", "name": "Queijo extra" },
        { "id": "PROD-BACON", "name": "Bacon" }
    ],
    "optionGroups": [
        { "id": "GRUPO-ID", "name": "Adicionais", "status": "AVAILABLE", "optionIds": ["OPT-QUEIJO", "OPT-BACON"] }
    ],
    "options": [
        { "id": "OPT-QUEIJO", "productId": "PROD-QUEIJO", "status": "AVAILABLE", "index": 0, "price": { "value": 4.00 } },
        { "id": "OPT-BACON", "productId": "PROD-BACON", "status": "AVAILABLE", "index": 1, "price": { "value": 6.00 } }
    ]
}

product.optionGroups[].min/max é quem define se o grupo é obrigatório (min ≥ 1) e quantas opções podem ser escolhidas. optionGroups[].optionGroupType pode ser omitido pra um grupo genérico — o iFood aplica DEFAULT automaticamente.

Combos e pizzas não aceitam grupo genérico: itens do tipo COMBO_V2 ou PIZZA exigem um optionGroupType explícito dentre os valores próprios de cada tipo (ver seções abaixo) — omitir, ou usar o padrão genérico, resulta em "The OptionGroup '...' must have a declared 'type'...". Um grupo de complementos comum só funciona em itens DEFAULT.

Depois de criado, preço/status/código de uma opção específica podem ser atualizados sem reenviar o item inteiro — ver Preço de complemento.

Combo

Um combo (item.type: "COMBO_V2") é um item cujo "produto principal" tem um grupo obrigatório (o combo em si) e pode ter grupos adicionais — cada grupo usa optionGroupType: "OFFER_UNIT", e o grupo principal é marcado com associationType: "MAIN" no vínculo do produto.

JSON — combo com 2 grupos
{
    "item": { "id": "ITEM-ID", "type": "COMBO_V2", "categoryId": "CAT-ID", "productId": "COMBO-PROD-ID", "status": "AVAILABLE" },
    "products": [
        {
            "id": "COMBO-PROD-ID", "name": "Combo Clássico",
            "optionGroups": [
                { "id": "GRUPO-LANCHE", "min": 1, "max": 1, "associationType": "MAIN" },
                { "id": "GRUPO-BEBIDA", "min": 1, "max": 1 }
            ]
        },
        { "id": "PROD-HAMBURGUER", "name": "Hambúrguer" },
        { "id": "PROD-BACON-BURGUER", "name": "Hambúrguer de bacon" },
        { "id": "PROD-REFRI-LARANJA", "name": "Refrigerante de laranja" },
        { "id": "PROD-REFRI-UVA", "name": "Refrigerante de uva" }
    ],
    "optionGroups": [
        { "id": "GRUPO-LANCHE", "name": "Escolha seu hambúrguer", "status": "AVAILABLE", "optionGroupType": "OFFER_UNIT", "optionIds": ["OPT-HAMBURGUER", "OPT-BACON-BURGUER"] },
        { "id": "GRUPO-BEBIDA", "name": "Escolha seu refrigerante", "status": "AVAILABLE", "optionGroupType": "OFFER_UNIT", "optionIds": ["OPT-REFRI-LARANJA", "OPT-REFRI-UVA"] }
    ],
    "options": [
        { "id": "OPT-HAMBURGUER", "productId": "PROD-HAMBURGUER", "status": "AVAILABLE", "index": 0, "price": { "value": 15.00 } },
        { "id": "OPT-BACON-BURGUER", "productId": "PROD-BACON-BURGUER", "status": "AVAILABLE", "index": 1, "price": { "value": 18.00 } },
        { "id": "OPT-REFRI-LARANJA", "productId": "PROD-REFRI-LARANJA", "status": "AVAILABLE", "index": 0, "price": { "value": 8.00 } },
        { "id": "OPT-REFRI-UVA", "productId": "PROD-REFRI-UVA", "status": "AVAILABLE", "index": 1, "price": { "value": 8.00 } }
    ]
}

O preço final do combo é a soma das opções escolhidas em cada grupo — o item em si não tem price próprio.

Pizza

Um item de pizza (item.type: "PIZZA") tem, no mínimo, os grupos SIZE, CRUST, EDGE e TOPPING — mesmo que EDGE seja opcional (min: 0), o grupo precisa existir, senão o iFood rejeita com "Pizza item must have sizes, crusts and edges".

Nunca envie "categoryId" num item de pizza. O iFood sempre cria uma categoria nova automaticamente pra cada pizza — mesmo passando o id de uma categoria já existente (com template: "PIZZA"), a resposta é 409 "Cannot have two categories with same id". Capture o categoryId devolvido na resposta se precisar dele depois.
Vincular sabor a tamanho usa "parentOptionId", não "parentCustomizationOptionId": cada opção de sabor (grupo TOPPING) precisa de um contextModifiers[] com uma entrada por tamanho, usando o campo parentOptionId apontando pro id da opção de tamanho correspondente. parentCustomizationOptionId é um nome parecido que só existe nos endpoints de PATCH de preço/status de opção — usá-lo aqui (na criação) resulta em "Pizza topping must be linked with a size", sem indicar qual campo está errado.
JSON — pizza com 2 tamanhos, 2 massas, 2 bordas e 2 sabores
{
    "item": { "id": "ITEM-ID", "type": "PIZZA", "productId": "PIZZA-PROD-ID", "status": "AVAILABLE" },
    "products": [
        {
            "id": "PIZZA-PROD-ID", "name": "Calabresa",
            "optionGroups": [
                { "id": "GRUPO-TAMANHO", "min": 1, "max": 1 },
                { "id": "GRUPO-MASSA", "min": 1, "max": 1 },
                { "id": "GRUPO-BORDA", "min": 0, "max": 1 },
                { "id": "GRUPO-SABOR", "min": 1, "max": 1 }
            ]
        },
        { "id": "PROD-TAM-P", "name": "Pequena" }, { "id": "PROD-TAM-G", "name": "Grande" },
        { "id": "PROD-MASSA-TRAD", "name": "Massa Tradicional" }, { "id": "PROD-MASSA-FINA", "name": "Massa Fina" },
        { "id": "PROD-BORDA-TRAD", "name": "Borda Tradicional" }, { "id": "PROD-BORDA-CATUPIRY", "name": "Borda Catupiry" },
        { "id": "PROD-CALABRESA", "name": "Calabresa" }, { "id": "PROD-MARGHERITA", "name": "Margherita" }
    ],
    "optionGroups": [
        { "id": "GRUPO-TAMANHO", "name": "Tamanho", "status": "AVAILABLE", "optionGroupType": "SIZE", "optionIds": ["OPT-TAM-P", "OPT-TAM-G"] },
        { "id": "GRUPO-MASSA", "name": "Massa", "status": "AVAILABLE", "optionGroupType": "CRUST", "optionIds": ["OPT-MASSA-TRAD", "OPT-MASSA-FINA"] },
        { "id": "GRUPO-BORDA", "name": "Borda", "status": "AVAILABLE", "optionGroupType": "EDGE", "optionIds": ["OPT-BORDA-TRAD", "OPT-BORDA-CATUPIRY"] },
        { "id": "GRUPO-SABOR", "name": "Sabor", "status": "AVAILABLE", "optionGroupType": "TOPPING", "optionIds": ["OPT-CALABRESA", "OPT-MARGHERITA"] }
    ],
    "options": [
        { "id": "OPT-TAM-P", "productId": "PROD-TAM-P", "status": "AVAILABLE", "index": 0, "price": { "value": 25.00 }, "fractions": [1, 2] },
        { "id": "OPT-TAM-G", "productId": "PROD-TAM-G", "status": "AVAILABLE", "index": 1, "price": { "value": 42.00 }, "fractions": [1, 2, 3, 4] },
        { "id": "OPT-MASSA-TRAD", "productId": "PROD-MASSA-TRAD", "status": "AVAILABLE", "index": 0, "price": { "value": 0 } },
        { "id": "OPT-MASSA-FINA", "productId": "PROD-MASSA-FINA", "status": "AVAILABLE", "index": 1, "price": { "value": 2.00 } },
        { "id": "OPT-BORDA-TRAD", "productId": "PROD-BORDA-TRAD", "status": "AVAILABLE", "index": 0, "price": { "value": 0 } },
        { "id": "OPT-BORDA-CATUPIRY", "productId": "PROD-BORDA-CATUPIRY", "status": "AVAILABLE", "index": 1, "price": { "value": 8.00 } },
        {
            "id": "OPT-CALABRESA", "productId": "PROD-CALABRESA", "status": "AVAILABLE", "index": 0, "price": { "value": 0 },
            "contextModifiers": [
                { "catalogContext": "DEFAULT", "parentOptionId": "OPT-TAM-P", "price": { "value": 0 }, "status": "AVAILABLE" },
                { "catalogContext": "DEFAULT", "parentOptionId": "OPT-TAM-G", "price": { "value": 0 }, "status": "AVAILABLE" }
            ]
        },
        {
            "id": "OPT-MARGHERITA", "productId": "PROD-MARGHERITA", "status": "AVAILABLE", "index": 1, "price": { "value": 0 },
            "contextModifiers": [
                { "catalogContext": "DEFAULT", "parentOptionId": "OPT-TAM-P", "price": { "value": 0 }, "status": "AVAILABLE" },
                { "catalogContext": "DEFAULT", "parentOptionId": "OPT-TAM-G", "price": { "value": 0 }, "status": "AVAILABLE" }
            ]
        }
    ]
}
DetalheRegra confirmada
Tamanhos precisam de fractionsUm array com os "pedaços" válidos pra aquele tamanho, ex.: [1,2,3,4] pra permitir até 4 sabores diferentes numa pizza grande. Omitir gera "Pizza size must have fractions".
Sabor precisa de um contextModifiers por tamanhoFaltar o vínculo com qualquer tamanho cadastrado gera "Pizza topping option X must be linked with all sizes in all contexts".

Agendamento de disponibilidade (shifts)

Restringe a venda de um item a dias/horários específicos (ex.: só no almoço). É um campo do próprio item, enviado junto no PUT /items — não existe endpoint dedicado.

JSON — trecho do body
{
    "item": {
        "id": "ITEM-ID", "type": "DEFAULT", "categoryId": "CAT-ID", "productId": "PROD-ID", "status": "AVAILABLE",
        "shifts": [
            { "startTime": "11:00", "endTime": "15:00", "monday": true, "tuesday": true, "wednesday": true, "thursday": true, "friday": true, "saturday": false, "sunday": false }
        ]
    },
    "products": [ ... ]
}
Omitir "shifts" não preserva a configuração anterior: o PUT /items é um replace completo — se você reconstruir o item pra alterar só o nome ou a foto e esquecer de incluir shifts com o valor atual, o iFood reseta pro padrão dele (00:00–23:59, todos os dias — que na prática já significa "sempre disponível"). Pra restaurar "sempre disponível" de propósito, basta mandar shifts: [] — o iFood aplica esse mesmo padrão automaticamente. Se quiser manter uma restrição já configurada, busque o item atual primeiro (GET .../items/{itemId}/flat) e reenvie o shifts dele junto com o resto do payload.

Obter item completo

Devolve um item com produto, grupos de opção e opções já resolvidos — útil pra reconstruir o payload de um PUT antes de alterar só um campo (ver aviso acima sobre shifts).

GET /api/catalog/{merchantId}/items/{itemId}/flat

Exemplo

cURL
curl https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/items/eb09eba5-bdc8-4cb3-aacc-77a31017a9eb/flat \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta: { "item": {...}, "products": [...], "optionGroups": [...], "options": [...] } — mesmo formato do body de PUT /items, pronto pra editar e reenviar.

Itens vendáveis

Lista os itens ativos e disponíveis pra venda num catálogo agora.

GET /api/catalog/{merchantId}/catalogs/{catalogId}/sellable-items

Itens bloqueados

Lista os itens indisponíveis num catálogo, agrupados por categoria, com o motivo do bloqueio em cada um — útil pra diagnosticar por que um item não aparece no app do iFood sem precisar adivinhar.

GET /api/catalog/{merchantId}/catalogs/{catalogId}/unsellable-items

Resposta de sucesso

JSON (200)
{
    "categories": [
        {
            "name": "Sobremesas",
            "items": [
                { "id": "...", "name": "Pudim", "restrictions": ["ITEM_PAUSED"] }
            ]
        }
    ]
}

Motivos comuns em restrictions: CATEGORY_PAUSED, ITEM_PAUSED, ITEM_PRICE_MISSING, OPTION_GROUP_WITHOUT_AVAILABLE_OPTIONS, OPTION_PAUSED.

Preço de item

Atualiza o preço de um item já existente, sem precisar reenviar o item inteiro.

PATCH /api/catalog/{merchantId}/items/price

Body (JSON) — array de objetos

CampoTipoObrigatórioDescrição
itemIdstringSimO id do item (não productId nem externalCode — o iFood rejeita esses como chave aqui).
price.valuenumberSimNovo preço.
priceByCatalogarrayNãoOverrides de preço por contexto/canal, ex.: [{"value":29.9,"catalogContext":"WHITELABEL"}].
"price" (raiz) e "contextModifiers[DEFAULT].price" são campos independentes: confirmado por teste real — atualizar um não atualiza o outro automaticamente. O iHub já contorna isso: toda chamada aqui inclui um override explícito de priceByCatalog pro contexto DEFAULT mesmo que você não peça, sincronizando os dois campos. Se você ler o preço de volta, priorize contextModifiers[DEFAULT].price sobre item.price — é o que reflete atualizações em lote (ver abaixo).

Exemplo

cURL
curl -X PATCH https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/items/price \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '[ { "itemId": "eb09eba5-bdc8-4cb3-aacc-77a31017a9eb", "price": { "value": 27.90 } } ]'

Resposta: {"results":[{"itemId":"...","success":true}]} com status 200 se tudo deu certo, ou 207 Multi-Status se algum item da lista falhou (cada resultado indica o próprio sucesso).

O iFood processa um item por chamada internamente (não é assíncrono/lote de verdade, apesar de aceitar uma lista) — pra atualizar centenas de itens de uma vez sem múltiplas idas e vindas, use atualização em massa abaixo.

Status (disponibilidade) de item

Mesmo formato de preço de item, pra pausar/reativar.

PATCH /api/catalog/{merchantId}/items/status

Exemplo

cURL
curl -X PATCH https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/items/status \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '[ { "itemId": "eb09eba5-bdc8-4cb3-aacc-77a31017a9eb", "status": "UNAVAILABLE" } ]'

Aceita statusByCatalog igual a priceByCatalog, pra pausar só num canal específico.

Código externo do item

Atualiza o código do seu PDV vinculado a um item.

PATCH /api/catalog/{merchantId}/items/external-code

Body (JSON)

CampoTipoObrigatório
itemIdstringSim
externalCodestring (máx. 100)Sim

Preço de complemento

Mesma lógica de preço de item, mas pra uma opção (complemento) dentro de um grupo.

PATCH /api/catalog/{merchantId}/options/price

Body (JSON)

CampoTipoObrigatório
optionIdstringSim
price.valuenumberSim

Assim como em itens, o iHub sincroniza automaticamente com contextModifiers[DEFAULT] nessa chamada.

Status de complemento

PATCH /api/catalog/{merchantId}/options/status

Body: { "optionId": "...", "status": "AVAILABLE" | "UNAVAILABLE" }.

Código externo do complemento

PATCH /api/catalog/{merchantId}/options/external-code

Body: { "optionId": "...", "externalCode": "..." } (máx. 100 caracteres).

Atualização de preço em massa

Diferente do PATCH /items/price: esse é o endpoint de lote de verdade — assíncrono, processa centenas de itens numa única chamada e devolve um batchId pra acompanhar.

PATCH /api/catalog/{merchantId}/products/price

Body (JSON) — array de objetos

CampoTipoObrigatórioDescrição
price.valuenumberSimNovo preço.
externalCodestringUm dos doisIdentifica o produto pelo código do seu PDV.
productIdstringUm dos doisOu identifica pelo id do produto no iFood.
Só atualiza "contextModifiers", não a raiz: ao contrário do PATCH /items/price (que sincroniza os dois), esse endpoint de lote grava só em contextModifiers[DEFAULT].price. Se sua aplicação lê item.price (raiz) em vez de contextModifiers, o valor vai parecer desatualizado depois de um lote — sempre priorize contextModifiers[DEFAULT] na leitura.

Exemplo

cURL
curl -X PATCH https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/products/price \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Content-Type: application/json" \
  -d '[
    { "externalCode": "SKU-001", "price": { "value": 27.90 } },
    { "externalCode": "SKU-002", "price": { "value": 15.50 } }
  ]'

Resposta de sucesso

Status 202 Accepted com {"batchId": "..."} — consulte o progresso em Consultar lote.

Atualização de status em massa

PATCH /api/catalog/{merchantId}/products/status

Mesmo formato do lote de preço: [{ "externalCode": "...", "status": "AVAILABLE" | "UNAVAILABLE" }], resposta 202 com batchId.

Consultar status do lote

GET /api/catalog/{merchantId}/batch/{batchId}

Exemplo

cURL
curl https://ihub.arcn.com.br/api/catalog/abcd-1234-efgh-5678/batch/batch-uuid-aqui \
  -H "Authorization: Bearer SEU_TOKEN_SECRETO" \
  -H "Accept: application/json"

Resposta de sucesso

JSON (200)
{
    "batchStatus": "COMPLETED",
    "results": [
        { "resourceId": "SKU-001", "result": "SUCCESS" },
        { "resourceId": "SKU-002", "result": "SUCCESS" }
    ]
}

batchStatus passa por PROCESSING antes de COMPLETED — normalmente leva poucos segundos pra lotes de até algumas centenas de itens. Faça polling com um intervalo curto (ex.: 2s) até sair de PROCESSING.

Inventário

Define um limite de estoque pra um produto parar de vender automaticamente quando esgotar.

POST /api/catalog/{merchantId}/inventory

Body: { "productId": "...", "amount": 10 }. Status 201.

GET /api/catalog/{merchantId}/inventory/{productId}

Consulta o estoque atual do produto.

POST /api/catalog/{merchantId}/inventory/batch-delete

Remove o controle de estoque (volta a vender sem limite) de um ou mais produtos. Body: { "productIds": ["...", "..."] }.

Upload de imagem

Envia uma imagem (JPG/PNG, até 5MB) em base64 e devolve um caminho reutilizável em products[].imagePath.

POST /api/catalog/{merchantId}/image

Body (JSON)

CampoTipoObrigatórioDescrição
imagestringSimImagem em base64 (com ou sem o prefixo data:image/...;base64,).

Resposta de sucesso

Status 201 Created com {"imagePath": "..."}.

O caminho devolvido aqui não é a URL final: o valor de imagePath retornado por esse upload é um caminho relativo do storage do iFood, não a URL pública do CDN. Pra pegar a URL absoluta (a mesma que aparece quando você consulta o item depois), associe esse imagePath ao produto via PUT /items e depois releia o item com GET .../items/{itemId}/flat — o imagePath que volta ali já é a URL final resolvida.