Erros e troubleshooting Diagnostique, resolva e previna problemas comuns ao integrar com a API de Promotion. Códigos de erro HTTP 202Accepted Requisição enfileirada para processamento Consulte GET com aggregationId para status real 400Bad Request Erro de validação no payload Revise campos obrigatórios e formato 401Unauthorized Token JWT inválido ou expirado Renovar token de acesso 404Not Found Loja (merchantId) ou agregação não existe Verificar os IDs 412Precondition Failed Validação falhou (ex: formato de data) Verificar campos e formatos 429Too Many Requests Limite de requisições excedido Implementar backoff exponencial 500Internal Server Error Erro no backend Contatar suporte com aggregationId
Códigos de erro de processamento Quando uma promoção tem status ERROR, o campo error contém um código específico: DATE_INVALIDData em formato inválido ou finalDate < initialDate Use ISO 8601 (YYYY-MM-DD) e garanta initialDate ≤ finalDate PROMOTION_TYPE_INVALIDTipo não reconhecido Usar PERCENTAGE, LXPY ou PERCENTAGE_PER_X_UNITS DISCOUNT_INVALIDDesconto > 70% ou campos faltantes Verificar regras de validação por tipo ITEM_NOT_FOUNDEAN não existe ou produto inativo Validar EAN e confirmar que produto está ativo DUPLICATE_ITEMMesmo EAN e período já existe Verificar promoções existentes com GET ou usar reset=true INVALID_CHANNELCanal não válido Usar canais válidos (ex: IFOOD-APP) INTERNAL_ERRORFalha no backend Aguardar e reenviar. Se persistir, contatar suporte
Cenários comuns de erro Cada cenário abaixo inclui sintomas, diagnóstico e solução prática com exemplos de código. Sintomas: Você criou uma promoção, mas ela não aparece no aplicativo do cliente.Diagnóstico: Verifique o status da promoção:
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items" \
-H "Authorization: Bearer {TOKEN}" Analise o campo status: PROCESSING → Ainda sendo validada. Aguarde 2-3 segundos.ERROR → Verifique campo error para código específico.ACTIVE → Validada, mas pode não estar visível em pedidos.Solução: Se PROCESSING: Aguarde alguns segundos e consulte novamente
sleep 3
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items" \
-H "Authorization: Bearer {TOKEN}" Se ERROR: Consulte o código no campo error
{
"promotions" : [
{
"status" : "ERROR" ,
"error" : "DATE_INVALID"
}
]
} Se ACTIVE: Prossiga para "Desconto não aparece em pedidos" abaixo.Resposta 202 mas promoção nunca fica ACTIVE Sintomas: A requisição POST retorna 202 (sucesso), mas o status permanece PROCESSING ou ERROR indefinidamente.Causas prováveis: Processamento assíncrono ainda em andamento (primeiras tentativas) EAN incorreto ou não existe no catálogo Data em formato inválido Desconto superior a 70% Campos obrigatórios para o tipo de promoção faltando Solução: Aguarde tempo adequado após POSTPrimeiras tentativas (0-10s): Pode estar em PROCESSING Após 30s: Se ainda PROCESSING, investigar
sleep 10
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items" \
-H "Authorization: Bearer {TOKEN}" Se o status for ERROR , verifique o código:
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items?status=ERROR" \
-H "Authorization: Bearer {TOKEN}" Corrija o payload baseado no código de erro
curl -X POST "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions" \
-H "Authorization: Bearer {TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"aggregationTag": "promo-corrigido-001",
"promotions": [ ... ]
}' Use novo aggregationTag para cada tentativa (não reutilize anterior).Desconto não aparece em pedidos Sintomas: A promoção tem status ACTIVE, mas o cliente não vê o desconto aplicado ao fazer uma compra.Causas prováveis: Outra promoção com desconto maior está ativa para o mesmo item Regras de elegibilidade da loja excluem o item Desconto não foi aplicado à categoria correta Diagnóstico: Verifique se há múltiplas promoções ativas para este EAN
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items?ean=7891234567890&status=ACTIVE" \
-H "Authorization: Bearer {TOKEN}" Crie um pedido de teste e verifique o benefício aplicado
# Consultar detalhes do pedido
curl -X GET "https://merchant-api.ifood.com.br/order/v1.0/merchants/{merchantId}/orders/{orderId}/virtual-bag" \
-H "Authorization: Bearer {TOKEN}" Analise a resposta procurando por:
{
"bag" : {
"benefits" : {
"benefits" : [
{
"target" : "ITEM" ,
"targetId" : "7891234567890" ,
"sponsorships" : [
{
"liability" : "PARTNER" ,
"amount" : {
"value" : 5.00
}
}
]
}
]
}
}
} Solução: Se múltiplas promoções: A plataforma aplica o desconto maior automaticamenteRemova promoções conflitantes usando reset=true Ou ajuste os descontos para não competir Se desconto não aparece: Verifique regras de negócioRate Limit excedido (HTTP 429) Sintomas: Requisições retornam 429 Too Many Requests.Causa: Limite de requisições por unidade de tempo foi excedido.Solução: Implemente backoff exponencial para retries:
#!/bin/bash
# Exemplo de retry com backoff exponencial
function call_api_with_retry() {
local url= " $1 "
local data= " $2 "
local attempt=1
local max_attempts=5
while [ $attempt -le $max_attempts ]; do
# Fazer a requisição
response= $(curl -s -w "\n%{http_code}" -X POST " $url " \
-H "Authorization: Bearer {TOKEN}" \
-H "Content-Type: application/json" \
-d " $data ")
# Extrair código HTTP
http_code= $(echo " $response " | tail -n1)
body= $(echo " $response " | head -n-1)
if [ " $http_code " != "429" ]; then
echo " $body "
return 0
fi
# Calcular tempo de espera: 2^attempt segundos
wait_time= $(( 2 ** attempt))
echo "Rate limited. Aguardando ${wait_time} s antes da tentativa $((attempt + 1 ))..." >&2
sleep " $wait_time "
((attempt ++ ))
done
echo "Falha após $max_attempts tentativas" >&2
return 1
} Prevenção: Não faça requisições em paralelo em massa Implemente fila com delay entre requisições Monitore a taxa de requisições da sua aplicação Token expirado (HTTP 401) Sintomas: Todas as requisições retornam 401 Unauthorized.Causas: Token JWT expirou Token foi revogado Token não tem escopo necessário Solução: Renove o token de acesso
curl -X POST "https://oauth.ifood.com.br/oauth/authorize" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}" Reenvie com novo token
curl -X GET "https://merchant-api.ifood.com.br/promotion/v1.0/merchants/{merchantId}/promotions/{aggregationId}/items" \
-H "Authorization: Bearer {NEW_TOKEN}" Prevenção: Renove token antes de expiração (ex: 5 minutos antes) Configure alertas de expiração próxima Não reutilize tokens por muito tempo Boas práticas para minimizar erros 1. Validação local antes de enviar Valide dados antes de fazer a requisição:
function validatePromotion ( promo ) {
// Validar formato de data
const dateRegex = / ^ \d {4} -\d {2} -\d {2} $ / ;
if (! dateRegex . test ( promo . initialDate )) {
throw new Error ( 'Data inválida: use YYYY-MM-DD' );
}
// Validar datas lógicas
if ( new Date ( promo . initialDate ) > new Date ( promo . finalDate )) {
throw new Error ( 'initialDate não pode ser posterior a finalDate' );
}
// Validar desconto
if ( promo . discountValue > 70 ) {
throw new Error ( 'Desconto não pode exceder 70%' );
}
// Validar tipo de promoção
const validTypes = [ 'PERCENTAGE' , 'LXPY' , 'PERCENTAGE_PER_X_UNITS' ];
if (! validTypes . includes ( promo . promotionType )) {
throw new Error ( `Tipo inválido. Use: ${ validTypes . join ( ', ' ) } ` );
}
return true ;
} 2. Logging estruturado Registre informações úteis para troubleshooting:
function logPromotion ( action , data , response ) {
console . log ( JSON . stringify ({
timestamp: new Date (). toISOString (),
action: action ,
aggregationId: data . aggregationTag || response . aggregationId ,
merchantId: data . merchantId ,
itemCount: data . promotions ?.[ 0 ]?. items ?. length || 0 ,
httpCode: response . status ,
errorCode: response . error ,
}, null , 2 ));
} 3. Estratégia de retry adequada
const retryConfig = {
maxAttempts: 3 ,
initialDelayMs: 1000 ,
backoffMultiplier: 2 ,
retryableStatusCodes: [ 429 , 500 , 502 , 503 , 504 ],
};
async function makeRequestWithRetry ( fn , config = retryConfig ) {
let attempt = 1 ;
let delayMs = config . initialDelayMs ;
while ( attempt <= config . maxAttempts ) {
try {
return await fn ();
} catch ( error ) {
if (! config . retryableStatusCodes . includes ( error . statusCode )) {
throw error ; // Não é retentável
}
if ( attempt === config . maxAttempts ) {
throw error ; // Última tentativa
}
await sleep ( delayMs );
delayMs *= config . backoffMultiplier ;
attempt ++;
}
}
} 4. Monitoramento e alertas Configure alertas para: Taxa de erros acima de X% Latência de resposta acima de limite Erros 5xx ou 429 recorrentes Promoções que ficam em PROCESSING por muito tempo Próximos passos Após resolver erros: Esta página foi útil? Sim Não
Avalie sua experiência no novo Developer portal: Avaliar agora