Webhooks
Guia de Integração de Webhooks
Receba notificações em tempo real sobre eventos de controle de acesso — criação, atualização, transferência, cancelamento, reembolso e uso de ingressos.
Visão geral
Webhooks permitem que seu sistema seja notificado automaticamente sempre que um evento relevante ocorre na plataforma de controle de acesso da Ingresse. Em vez de consultar a API repetidamente (polling), você registra uma URL e passa a receber requisições HTTP POST com o payload do evento.
Como funciona
Ambientes
| Ambiente | Base URL | Uso |
|---|---|---|
production | <WEBHOOK_PROD_HOST> | Fornecido junto com suas credenciais no onboarding. |
staging | https://awa-access-webhook.staging.ingresse.com | Ambiente de testes/homologação. |
Gatilhos disponíveis
Ao criar uma assinatura, escolha um ou mais gatilhos. Cada gatilho corresponde a um tipo de evento.
| Gatilho | event_type | Descrição |
|---|---|---|
create | code_created | Novo código/ingresso criado. |
update | code_updated | Código/ingresso atualizado. |
transfer | code_transferred | Ingresso transferido para outra pessoa. |
cancel | code_cancelled | Ingresso cancelado. |
refund | code_refunded | Ingresso reembolsado. |
use | code_used | Ingresso utilizado (check-in/validação). |
Disponibilidade dos gatilhos
Estrutura do payload
Cada notificação é enviada como POST com corpo JSON. Os campos de topo identificam o evento; payload contém os dados do código/ingresso.
{
"id": 1042,
"event_type": "code_created",
"created_at": "2026-04-29T13:00:00Z",
"payload": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"code": "25ABC12345678",
"external_id": "ext-code-123",
"type": "individual",
"status": "active",
"order_id": "650e8400-e29b-41d4-a716-446655440000",
"user_id": "750e8400-e29b-41d4-a716-446655440000",
"event_id": "850e8400-e29b-41d4-a716-446655440000",
"ticket_id": "950e8400-e29b-41d4-a716-446655440000",
"can_transfer": true,
"attributes": {
"rfc": true,
"rfc_provider": "biometry"
},
"owner": {
"id": "750e8400-e29b-41d4-a716-446655440000",
"name": "João Silva",
"email": "[email protected]"
},
"user": {
"id": "750e8400-e29b-41d4-a716-446655440000",
"name": "João Silva",
"email": "[email protected]"
},
"event": {
"id": "95a447c4-0000-4000-8000-000000000002",
"external_id": "850e8400-e29b-41d4-a716-446655440000",
"name": "Rock in Rio 2026"
},
"ticket": {
"id": "950e8400-e29b-41d4-a716-446655440000",
"name": "Pista Premium"
}
}
}| Campo | Tipo | Descrição |
|---|---|---|
id | int | Identificador da notificação. |
event_type | string | Tipo do evento (ex: code_created). |
created_at | string (ISO 8601) | Momento em que a plataforma ingeriu o evento (não necessariamente o instante exato em que ele ocorreu). |
payload | object | Dados do código/ingresso afetado. |
Os objetos aninhados user, owner, event e ticket são opcionais: dependendo do gatilho e do contexto, podem estar ausentes do payload. O evento pode também trazer campos adicionais além dos documentados aqui (por exemplo owner_id, buyer_id, ownership, validation_metadata, updated_at) — ignore campos desconhecidos ao processar o payload.
O campo event_id de topo e o external_id dentro do objeto event são o UUID externo do evento — o mesmo identificador usado nas URLs da Access Control API; event.id é um identificador interno, não use para correlacionar.
Backfill ao criar uma assinatura
Segurança e validação de assinatura
Toda requisição inclui o header X-Signature no formato sha256=<hash>. O hash é um HMAC-SHA256 do corpo bruto da requisição usando o secret da assinatura. Sempre valide a assinatura antes de processar o evento.
O secret é gerado no momento da criação da assinatura e entregue pela Ingresse durante o onboarding.
Use o corpo bruto
const crypto = require('crypto')
function verificarAssinatura(segredo, corpo, cabecalho) {
const esperado = Buffer.from('sha256=' + crypto.createHmac('sha256', segredo).update(corpo).digest('hex'))
const recebido = Buffer.from(cabecalho ?? '')
// timingSafeEqual lança se os buffers tiverem tamanhos diferentes — comparar antes.
if (esperado.length !== recebido.length) return false
return crypto.timingSafeEqual(esperado, recebido)
}
// Com Express: use express.raw para receber o corpo bruto
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
if (!verificarAssinatura(process.env.SEGREDO, req.body, req.headers['x-signature'] ?? ''))
return res.status(401).send()
// processar evento de forma assíncrona...
res.status(200).send()
})Idempotência
A mesma notificação pode ser entregue mais de uma vez (por exemplo, após uma reentrega). Use o campo id como chave de idempotência para evitar processamento duplicado. O id do envelope é estável entre reentregas: a mesma notificação sempre chega com o mesmo id.
# Exemplo com Redis (Python)
def processar(evento):
chave = f"webhook:{evento['id']}"
if redis.set(chave, 1, ex=86400, nx=True) is None:
return # já processado
# processar...Reentregas e timeouts
Responda com status 2xx o mais rápido possível (idealmente em poucos segundos) e processe o evento de forma assíncrona. O tempo de resposta considerado é de aproximadamente 25 segundos; acima disso a entrega é tratada como timeout. Respostas 3xx contam como sucesso — prefira responder 2xx diretamente.
Respostas 5xx ou timeout são reentregues automaticamente: a entrega é tentada até 3 vezes no total (a tentativa original mais 2 reentregas), com intervalo fixo de aproximadamente 2 minutos. Respostas 4xx (incluindo 429) são consideradas definitivas e não são reentregues. Após esgotadas as tentativas, a entrega é encaminhada para reprocessamento manual pela Ingresse.
Boas práticas
Filtros
É possível restringir quais eventos sua URL recebe usando o objeto filters na criação da assinatura. Apenas eventos cujo payload corresponda aos filtros são entregues.
| Filtro | Tipo | Descrição |
|---|---|---|
type | string | Tipo do código (ex: individual). |
status | string | Status do ingresso (ex: cancelled). |
event_id | string (UUID) | Restringe a um evento. |
ticket_id | string (UUID) | Restringe a um tipo de ingresso. |
can_transfer | boolean | Somente transferíveis/não transferíveis. |
attributes.rfc | boolean | Biometria facial ativa. |
Somente ingressos individuais com biometria ativa
{
"filters": {
"type": "individual",
"attributes": {
"rfc": true
}
}
}Somente ingressos com status cancelado
{
"filters": {
"status": "cancelled"
}
}Tipo de ingresso específico, apenas transferíveis
{
"filters": {
"ticket_id": "950e8400-e29b-41d4-a716-446655440000",
"can_transfer": true
}
}Todos os ingressos de um evento com biometria obrigatória
{
"filters": {
"event_id": "850e8400-e29b-41d4-a716-446655440000",
"attributes": {
"rfc": true
}
}
}