| Método | Fluxo | Melhor para |
|---|---|---|
| Webhook (Heartbeat) | Você responde aos heartbeats enviados pelo iFood | Controle granular, sistemas distribuídos, múltiplos merchants com status diferentes |
| Polling | Você faz requisições regulares ao iFood | Sistemas centralizados, poucos merchants, arquitetura mais simples |
KEEPALIVE) ao seu webhook. Você responde indicando quais merchants estão online.Fluxo:KEEPALIVE a cada 30 segundos202 Accepted → merchant(s) ficam online202 Accepted marca todos os merchants como online.Quando usar: Sistema sempre disponível, sem necessidade de controle individualizado.Request do iFood:{
"code": "KEEPALIVE",
"fullCode": "KEEPALIVE",
"id": "a38ba215-f949-4b2c-982a-0582a9d0c10e"
}HTTP/1.1 202 Accepted202 Accepted → todos os merchants ficam onlinecurl -X POST 'http://localhost:8080/webhook' \
-H 'X-IFood-Signature: 6c0a429606266905349388ebc1eb12b8b98524dddd6b4bdc41c4b67591075b4c' \
-H 'Content-Type: application/json' \
-d '{
"code": "KEEPALIVE",
"fullCode": "KEEPALIVE",
"id": "a38ba215-f949-4b2c-982a-0582a9d0c10e"
}'{
"code": "KEEPALIVE",
"fullCode": "KEEPALIVE",
"id": "a38ba215-f949-4b2c-982a-0582a9d0c10e",
"merchantIds": [
"cad65e8f-6fc6-438a-b159-e64a902a6b9a",
"438a5e8f-6fc6-438a-b159-e64a902a6879"
]
}HTTP/1.1 202 Accepted
Content-Type: application/json
{
"merchantIds": ["cad65e8f-6fc6-438a-b159-e64a902a6b9a"]
}202 Accepted com JSON contendo merchantIdscurl -X POST 'http://localhost:8080/webhook' \
-H 'X-IFood-Signature: dc900bca38c8b6f908ea688a24604fa581921d1211da4211e4f5e139732c2ff1' \
-H 'Content-Type: application/json' \
-d '{
"code": "KEEPALIVE",
"fullCode": "KEEPALIVE",
"id": "a38ba215-f949-4b2c-982a-0582a9d0c10e",
"merchantIds": [
"cad65e8f-6fc6-438a-b159-e64a902a6b9a",
"438a5e8f-6fc6-438a-b159-e64a902a6879"
]
}'| Aspecto | Webhook | Polling |
|---|---|---|
| Iniciativa | iFood envia heartbeats | Você consulta status |
| Frequência | A cada 30 segundos | A cada 30 segundos (recomendado) |
| Latência | Quase imediata | Até 30 segundos de atraso |
| Complexidade | Moderada (validar assinatura) | Simples (chamadas HTTP regulares) |
| Controle granular | Por aplicativo ou por merchant | Automático (enquanto polling ativo) |
| Overhead de rede | Baixo (iFood inicia) | Moderado (você inicia) |
| Ideal para | Múltiplos merchants com status diferentes | Poucos merchants, arquitetura simples |
| Manutenção | Configure no Developer Portal | Configure apenas o intervalo |
X-IFood-Signature como qualquer outro evento. Valide a assinatura antes de processar a requisição.Veja validação de assinatura para detalhes.@app.route('/webhook', methods=['POST'])
def webhook():
event = request.json
if event['code'] == 'KEEPALIVE':
# Modo por aplicativo: responda 202 para todos os merchants
return '', 202
# ... processar outros eventos@app.route('/webhook', methods=['POST'])
def webhook():
event = request.json
if event['code'] == 'KEEPALIVE':
if 'merchantIds' in event:
# Modo por merchant: filtre por horário
online_merchants = [
m for m in event['merchantIds']
if is_merchant_open(m)
]
return jsonify({'merchantIds': online_merchants}), 202
else:
# Modo por aplicativo: todos os abertos
return '', 202
# ... processar outros eventos@app.route('/webhook', methods=['POST'])
def webhook():
event = request.json
if event['code'] == 'KEEPALIVE':
if 'merchantIds' in event:
# Modo por merchant: responda com lista vazia
return jsonify({'merchantIds': []}), 202
else:
# Modo por aplicativo: responda com erro
return 'Service Unavailable', 503
# ... processar outros eventosimport time
from datetime import datetime
def keep_presence_active():
while True:
try:
# Fazer polling a cada 30 segundos
response = requests.get(
'https://merchant-api.ifood.com.br/events/v1.0/events:polling',
headers={'Authorization': f'Bearer {your_token}'},
headers={'x-polling-merchants': merchant_ids}
)
if response.status_code == 200:
events = response.json()
process_events(events)
# Enviar ACK para manter polling ativo
acknowledge_events(events)
elif response.status_code == 204:
# Sem eventos, mas presença mantida
pass
# Aguardar 30 segundos antes do próximo polling
time.sleep(30)
except Exception as e:
print(f"Erro no polling: {e}")
# Presença será perdida se polling falhar por muito tempo
time.sleep(30)
# A presença será mantida enquanto esse loop continuar rodando
keep_presence_active()x-polling-merchants para filtrar merchants específicos| Problema | Causa | Solução |
|---|---|---|
| Merchants aparecem como offline | Resposta sem 202 Accepted | Verifique status HTTP da resposta |
| Alguns merchants offline | Modo por merchant: IDs não retornados | Retorne todos os IDs dos merchants online |
| Heartbeats não chegam | Webhook não habilitado | Habilite webhook no Developer Portal |
| Signature inválida | Assinatura não corresponde | Valide usando a chave correta |
| Timeouts frequentes | Webhook respondendo lentamente | Otimize a resposta para < 5 segundos |
| Problema | Causa | Solução |
|---|---|---|
| Merchants offline apesar de polling ativo | Polling não está realmente rodando a cada 30s | Verifique logs e intervalos do loop |
| Polling retorna erro 429 | Limite de requisições excedido (6000 RPM) | Reduza frequência ou divida em lotes |
| Merchants offline após reinicialização | Polling parou durante deploy | Implemente health checks e recuperação automática |
| Token expirado durante polling | Token tem validade limitada | Implemente refresh de token antes de expirar |