Buscar na documentação
ctrl+4K
Módulos
Authentication
Merchant
Catalog
Order
Events
Logistics
Shipping
Review
Financial
Soluções

Boas práticas e troubleshooting

Recomendações para uma integração robusta e soluções para os problemas mais comuns.
O polling é o coração da integração. Implemente corretamente:Mínimo: 30 segundos
const POLLING_INTERVAL = 30000; // 30 segundos

setInterval(async () => {
  try {
    const events = await fetchEvents();
    for (const event of events) {
      await processEvent(event);
      await acknowledgeEvent(event.eventId);
    }
  } catch (error) {
    logger.error('Polling failed', error);
  }
}, POLLING_INTERVAL);
Menos de 30s causa rate limiting. Mais de 60s causa atraso na detecção de eventos. Consulte limites de rate limit.Sempre enviar acknowledge imediatamente após processar:
async function processEvent(event) {
  // 1. Processar
  await updateOrderStatus(event.orderId, event.type);

  // 2. Acknowledge IMEDIATAMENTE
  await acknowledgeEvent(event.eventId);

  // Sem acknowledge = reprocessamento contínuo
}
Implemente proteção contra eventos duplicados:
const processedEventIds = new Set();

async function processEvent(event) {
  if (processedEventIds.has(event.eventId)) {
    logger.info(`Event ${event.eventId} already processed`);
    return; // Ignorar duplicata
  }

  // ... processar evento ...

  processedEventIds.add(event.eventId);
}

// Limpar Set periodicamente (ex: a cada 24h)
setInterval(() => {
  processedEventIds.clear();
}, 24 * 60 * 60 * 1000);

Renovar 5 minutos antes de expiração:
const TOKEN_BUFFER = 5 * 60 * 1000; // 5 minutos

async function ensureValidToken() {
  if (!token || isExpiringSoon(token)) {
    token = await refreshToken();
    token.refreshedAt = Date.now();
  }
  return token;
}

function isExpiringSoon(token) {
  const expiresIn = token.expiresAt - Date.now();
  return expiresIn < TOKEN_BUFFER;
}
Não deixe tokens expirar antes de renovar. Não renove a cada requisição. Não reutilize tokens antigos.
Implementação recomendada:
async function requestWithRetry(fn, maxRetries = 5) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      // Erros transientes: retry
      if (isTransient(error)) {
        if (attempt < maxRetries) {
          const delay = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s, 8s...
          const jitter = Math.random() * 1000; // Evita thundering herd
          await sleep(delay + jitter);
          continue;
        }
      }
      // Erros permanentes: falhar imediatamente
      throw error;
    }
  }
}

function isTransient(error) {
  // 5xx, timeouts, rate limiting
  return error.status >= 500 ||
         error.status === 429 ||
         error.code === 'ECONNREFUSED';
}
Padrão recomendado:
TentativaDelayTotal
1Imediato0s
21s + jitter1s
32s + jitter3s
44s + jitter7s
58s + jitter15s
Sempre incluir contexto:
const logEntry = {
  timestamp: new Date().toISOString(),
  orderId: order.id,
  operation: 'requestDriver',
  status: 'success',
  duration: Date.now() - startTime,
  attempt: currentAttempt,
  error: error?.message,
  statusCode: response?.status,
};

logger.info('Shipping operation', logEntry);
Mantenha logs por mínimo 30 dias.
// Validar orderId
function isValidUUID(uuid) {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid);
}

// Validar telefone
function isValidPhone(countryCode, areaCode, number) {
  return countryCode === '55' && // Brasil
         areaCode.length === 2 &&
         (number.length === 8 || number.length === 9);
}

// Validar CEP
function isValidCEP(cep) {
  return /^\d{8}$/.test(cep.replace('-', ''));
}

// Validar coordenadas
function isValidCoordinates(lat, lng) {
  return lat >= -90 && lat <= 90 &&
         lng >= -180 && lng <= 180;
}

// Validar quoteId antes de usar
function isValidQuote(quote) {
  return quote &&
         isValidUUID(quote.id) &&
         new Date(quote.expirationAt) > new Date();
}

Enviar sempre um preparationTime realista:
1. Tempo médio de preparo (benchmarking)
   Ex: 12 minutos

2. Adicionar margem para picos
   Ex: +3 minutos

3. Converter para segundos
   (12 + 3) * 60 = 900 segundos
preparationTimeImpacto
Muito curtoEntregador chega antes; custa mais caro
Muito longoEntregador aguarda sem propósito
CorretoSinergia perfeita entre preparo e entrega
Semana 1: preparationTime = 900s (15 min)
  ↓ (Monitorar entregas)

Se entregador chega muito cedo:
  → Aumentar para 1000s (16 min)

Se entregador chega muito tarde:
  → Diminuir para 800s (13 min)

Pedido criado

REQUEST_DRIVER (202)

Polling... aguardando sucesso

REQUEST_DRIVER_SUCCESS
  ↓ (AGORA sim, consultar tracking)

GET /tracking
Nunca consultar tracking antes de REQUEST_DRIVER_SUCCESS!
// Após REQUEST_DRIVER_SUCCESS
setInterval(async () => {
  try {
    const tracking = await getTracking(orderId);
    updateUI(tracking);
  } catch (error) {
    if (error.status === 404) {
      logger.info('Tracking not yet available');
      // Entregador ainda sendo atribuído, retry depois
    }
  }
}, 30000); // 30 segundos
Campos podem retornar null durante atribuição:
const tracking = await getTracking(orderId);

// Seguro: verifica null antes de usar
if (tracking.latitude && tracking.longitude) {
  updateMapMarker(tracking.latitude, tracking.longitude);
}

// Pode ser null
if (tracking.expectedDelivery) {
  updateETA(tracking.expectedDelivery);
}

// Pode ser negativo (atraso em coleta)
const pickupDelay = tracking.pickupEtaStart < 0
  ? `Atrasado ${Math.abs(tracking.pickupEtaStart)}s`
  : `Faltam ${tracking.pickupEtaStart}s`;

1. Taxa de sucesso por endpoint
   - Alerta: < 95%
   - Intervalo: 1 hora

2. Latência P95
   - Alerta: > 5 segundos
   - Intervalo: 5 minutos

3. Taxa de erro por tipo
   - Alerta: HighDemand > 20%
   - Intervalo: 15 minutos

4. Polling falhas consecutivas
   - Alerta: > 3 falhas seguidas
   - Intervalo: tempo real

5. Taxa de timeout
   - Alerta: > 5%
   - Intervalo: 1 hora
1. Falta de renovação de token
   → Todas requisições falharão com 401

2. Polling parado
   → Eventos não serão processados

3. Taxa de erro > 10% por 30 min
   → Problema sistêmico em andamento

4. Atraso de evento > 5 min
   → Possível gargalo no sistema

Não considere uma operação bem-sucedida apenas pela resposta 202 Accepted. Sempre monitore os eventos para confirmar o resultado final.
// CORRETO
const response = await requestDriver(orderId, quoteId);
logger.info('Alocação solicitada, aguardando confirmação...');

// Depois, monitorar eventos
// Só confirmar quando receber REQUEST_DRIVER_SUCCESS
Usar Idempotency-Key em operações críticas:
async function cancelOrder(orderId) {
  const idempotencyKey = `cancel-${orderId}-${Date.now()}`;

  return fetch(`/orders/${orderId}/cancel`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Idempotency-Key': idempotencyKey
    },
    body: JSON.stringify({
      reason: 'Cliente solicitou',
      cancellationCode: 817
    })
  });
}

// Monitorar evento para obter código
function handleEvent(event) {
  if (event.type === 'DELIVERY_DROP_CODE_REQUESTED') {
    const code = event.metadata.CODE;

    // Notificar cliente via SMS/email/app
    sendCodeToCustomer(customer.phone, code);

    // Instruir entregador a solicitar código
    updateDeliveryInstructions(orderId,
      `Solicitar código ${code} antes de entregar`);
  }
}
Usar score para tomar decisões:
const safeDelivery = await getSafeDeliveryScore(orderId);

switch(safeDelivery.score) {
  case 'LOW':
    // Requerer validação adicional
    sendVerificationToCustomer(orderId);
    addAlert(`Entrega de risco baixo: ${orderId}`);
    break;

  case 'MODERATE':
    // Monitorar normalmente
    logger.info(`Moderate risk: ${orderId}`);
    break;

  case 'HIGH':
  case 'VERY_HIGH':
    // Processamento padrão
    break;
}

Pedidos na plataforma:
  [ ] GET /deliveryAvailabilities funciona
  [ ] POST /requestDriver retorna 202
  [ ] Eventos são recebidos
  [ ] GET /tracking funciona após sucesso
  [ ] Cancelamento funciona

Pedidos fora da plataforma:
  [ ] GET /merchants/{merchantId}/deliveryAvailabilities funciona
  [ ] POST /merchants/{merchantId}/orders retorna 202 + trackingUrl
  [ ] Código de confirmação é recebido
  [ ] Alterações de endereço funcionam
  [ ] Safe Delivery Score funciona

Erros:
  [ ] HighDemand é tratado com retry
  [ ] 404 em tracking é ignorado antes de sucesso
  [ ] Rate limiting é respeitado
  [ ] Tokens expirados são renovados

Monitoramento:
  [ ] Logs estruturados em todos pontos
  [ ] Alertas configurados
  [ ] Dashboard visível
  [ ] Métricas sendo coletadas

Para detalhes sobre rate limiting global e limites específicos do Shipping, consulte Rate limit.Resumo para Shipping:
OperaçãoLimite
Verificar disponibilidade4.000 req/min
Solicitar/Criar entrega4.000 req/min
Cancelamento600 req/min
Gerenciar endereços500 req/min
Rastreamento(incluído em polling)

Mensagem: "Sob Demanda indisponível: o endereço da entrega está a mais de 10 km da sua loja."Causa: O endereço está fora da cobertura de logística iFood.Soluções:
  • Confirme as coordenadas (latitude/longitude) enviadas
  • Valide o endereço com o cliente
  • Verifique se a região tem cobertura iFood disponível
  • Consulte o mapa de cobertura no Portal do Parceiro

Mensagem: "Endereço fora da cobertura iFood"Causa: A região não é atendida pelo serviço de logística iFood.Soluções:
  • Verifique cobertura iFood na região do cliente
  • Consulte mapa de cobertura iFood
  • Ofereça alternativa de logística para essa região

Mensagem: "Fora do horário de operação"Causa: A requisição foi feita fora do horário de funcionamento da logística.Soluções:
  • Tente novamente dentro do horário comercial
  • Configure alertas para horário de operação
  • Implemente retry automático com backoff exponencial
Horários de operação: Variam por região. Consulte o Portal do Parceiro.
Mensagem: "A logística iFood está temporariamente indisponível. Tente novamente mais tarde."Causa: Frota de entregadores saturada em picos de demanda.Soluções:
  • Implemente retry com backoff exponencial (2x, máx 8 tentativas)
  • Use preparationTime maior para distribuir demanda ao longo do tempo
  • Monitore horários de pico e planeje com antecedência
  • Considere oferecer alternativas de entrega
Estratégia recomendada:
Tentativa 1: 1s
Tentativa 2: 2s
Tentativa 3: 4s
Tentativa 4: 8s
Tentativa 5: 16s

Mensagem: "Loja não encontrada na área de logística"Causa: Sua loja não está registrada no sistema de logística iFood.Soluções:
  • Acesse Portal do Parceiro
  • Verifique se seu endereço de loja está correto
  • Registre sua loja nas áreas de logística disponíveis
  • Aguarde validação (pode levar 24-48 horas)
  • Contacte suporte se a loja continuar não encontrada

Mensagem: "Sua loja está temporariamente indisponível"Causa: Sua loja tem restrições de operação ou está temporariamente desativada.Soluções:
  • Verifique status da loja no Portal do Parceiro
  • Revise documentos pendentes (contrato, comprovante de endereço)
  • Contate suporte se houver dúvidas sobre status

Mensagem: "Forma de pagamento não suportada"Causa: O método de pagamento enviado não é válido ou não é aceito.Soluções:
  • Valide o método de pagamento contra a cotação
  • Use apenas CREDIT, DEBIT ou CASH
  • Consulte métodos disponíveis na resposta de disponibilidade
  • Verifique se bandeira de cartão é aceita (Visa, Mastercard, Elo)

Mensagem: "Limite de entregadores simultâneos atingido"Causa: Você atingiu o limite de alocações simultâneas contratado.Soluções:
  • Aguarde conclusão de entregas em andamento
  • Cancele entregas não viáveis
  • Contate suporte para aumentar limite
  • Monitore métricas de concorrência

Mensagem: "Serviço não habilitado para sua loja"Causa: O serviço de Shipping não está ativado na sua conta.Soluções:
Mensagem: "Houve um problema ao solicitar..."Causa: Dados enviados na requisição estão inválidos ou malformados.Soluções:
  • Valide o formato UUID do orderId ou merchantId
  • Verifique se todos campos obrigatórios foram enviados
  • Confirme tipos de dados (string vs. number vs. boolean)
  • Revise exemplo na documentação de endpoints
Validação de UUID:
Formato válido: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Exemplo: 57cd1046-2e06-446f-a2dd-18a518212c3c

Mensagem: "Cliente indisponível ou inválido"Causa: Dados de cliente inválidos ou cliente não pode receber entregas.Soluções:
  • Valide nome: máx 50 caracteres
  • Valide telefone:
    • País: 2 dígitos (ex: 55)
    • Área: 2 dígitos (ex: 11)
    • Número: 7-9 dígitos
  • Confirme que telefone é válido (não expirado, ativo)
  • Verifique se cliente não tem restrições

Mensagem: "Pedido não encontrado"Causa: Entregador ainda não foi atribuído ao pedido.Soluções:
  • Aguarde evento ASSIGN_DRIVER ou REQUEST_DRIVER_SUCCESS
  • Implemente retry após receber confirmação
  • Intervalo de retry recomendado: 30 segundos
  • Campos podem retornar null durante atribuição
Fluxo correto:
1. Solicitar entregador (202)
2. Aguardar evento REQUEST_DRIVER_SUCCESS (polling)
3. Depois disso, tracking fica disponível

Mensagem: "Alteração > 500m da coordenada original"Causa: Cliente solicitou mudança para mais de 500m de distância.Soluções:
  • Limite alterações a máximo 500m
  • Para distâncias maiores, cancele e recrie o pedido
  • Implemente validação no frontend antes de enviar

Mensagem: "Novo endereço em região diferente"Causa: Novo endereço cai em outra área de cobertura iFood.Soluções:
  • Valide se novo endereço mantém cobertura
  • Cancele e recrie pedido se mudar de região
  • Implemente validação de região no frontend

Mensagem: "Operação em conflito"Causa: Já existe uma alteração de endereço pendente.Soluções:
  • Aguarde resultado da alteração anterior
  • Monitore eventos DELIVERY_ADDRESS_CHANGE_*
  • Implemente fila de requisições
  • Timeout padrão: 15 minutos

Mensagem: "Unauthorized"Causa: JWT token expirado ou inválido.Soluções:
  • Renovar token antes de expiração
  • Implemente refresh automático (renovar 5 minutos antes de expirar)
  • Armazene expiresAt para controlar validade
  • Não reutilize tokens antigos

Mensagem: "Ops. Houve uma falha. Tente novamente em alguns instantes."Causa: Erro temporário no servidor iFood.Soluções:
  • Implemente retry com backoff exponencial
  • Máximo 3-5 tentativas
  • Intervalo: 1s, 2s, 4s, 8s, 16s
  • Log todas as tentativas
  • Após falhas, notifique suporte

Sintoma: Não estou recebendo eventos de entrega.Causas possíveis:
  • Intervalo de polling muito longo (>30s)
  • Eventos sendo consumidos em outro lugar
  • Event ID não está sendo reconhecido
  • Sistema não está fazendo acknowledge
Soluções:
  • Implemente polling a cada 30 segundos (nunca menos)
  • Use endpoint /polling corretamente
  • Implemente acknowledge imediato com /acknowledgment
  • Verifique se events têm eventId único
  • Implemente deduplicação por eventId
Checklist:
1. Polling a cada 30s?       ✓
2. Reconhecimento de events? ✓
3. Deduplicação por ID?      ✓
4. Logs de cada evento?      ✓

Mensagem: "Too Many Requests"Causa: Você excedeu limite de requisições do endpoint.Soluções:
  • Respeite intervalo de polling (mín 30s)
  • Implemente fila de requisições
  • Use backoff exponencial
  • Consulte limites específicos por endpoint
  • Monitore headers de rate limit na resposta
Headers importantes:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 50
X-RateLimit-Reset: 1693...

Sintoma: Requisição retorna 202, mas nunca recebo SUCCESS/FAILED.Causas possíveis:
  • Polling não implementado
  • Eventos sendo ignorados
  • quoteId expirado
  • Dados inválidos
Soluções:
  • Verifique polling está rodando
  • Confirme que eventos estão sendo processados
  • Valide quoteId ainda é válido (< 24h)
  • Revise logs para erro silencioso
  • Contacte suporte com orderId para investigar

Sintoma: Recebo REQUEST_DRIVER_SUCCESS, mas não consigo rastrear.Causas possíveis:
  • Sistema ainda processando alocação
  • Erro na atribuição em background
  • Timeout antes de ASSIGN_DRIVER
Soluções:
  • Aguarde evento ASSIGN_DRIVER (pode levar alguns minutos)
  • Implemente retry no tracking (máx 5 tentativas)
  • Intervalo entre tentativas: 30-60s
  • Se timeout continuar, verifique cobertura
  • Contacte suporte com orderId

GET /deliveryAvailabilities
  ↓ (valida cobertura + pega quoteId)

POST /requestDriver (com quoteId)
Nunca pule essa etapa.
async function requestWithRetry(fn, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (isTransient(error) && attempt < maxRetries) {
        const delay = Math.pow(2, attempt - 1) * 1000;
        await sleep(delay);
      } else {
        throw error;
      }
    }
  }
}

function isTransient(error) {
  return error.status >= 500 || error.status === 429;
}
- Taxa de sucesso por endpoint
- Latência P95 (95º percentil)
- Erros por tipo
- Tempo de polling a processamento
- Taxa de timeout
timestamp: 2023-08-17T20:10:00Z
orderId: 57cd1046-2e06-446f-a2dd-18a518212c3c
operation: requestDriver
status: success
duration: 150ms

Para dúvidas técnicas:Para problemas operacionais:
Esta página foi útil?
Avalie sua experiência no novo Developer portal:
Nesta página
Boas práticas e troubleshooting
Conteúdo lido0%