Estrutura do evento
Todos os eventos de webhook seguem esta estrutura:
{
id : string , // UUID - ID único do evento
event : string , // Tipo de evento: RAMP | USER | TRANSACTION | ACCOUNT | CUSTODIAL_ACCOUNT
action : string , // Ação: CREATE | UPDATE | DELETE
data : object , // Dados específicos do evento
createdAt : string , // Timestamp ISO 8601
updatedAt : string , // Timestamp ISO 8601
attempts : number // Contagem de tentativas de entrega (0 a 5)
}
Use o campo id para idempotência. O campo attempts indica a contagem de tentativas de retry.
Tipos de evento
Eventos RAMP
Tipo: RAMP
Ações:
CREATE - Ramp criado
UPDATE - Status do ramp mudou
DELETE - Ramp cancelado (raro)
Exemplo - Ramp criado:
{
"id" : "evt_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" ,
"event" : "RAMP" ,
"action" : "CREATE" ,
"data" : {
"id" : "be4d353b-00a2-4309-9ef1-594f37dfb1fd" ,
"userId" : "2bc703f2-1c54-4cbb-a144-993e1d957688" ,
"accountId" : "f7df00af-5d12-4b33-b5ab-e2b32290ebad" ,
"quotationId" : "a656a475-0d53-479e-8f30-91582ecf450b" ,
"type" : "ON" ,
"status" : "CREATED" ,
"cashInMethod" : "PSE" ,
"cashOutMethod" : "POLYGON" ,
"fromCurrency" : "COP" ,
"toCurrency" : "USDC" ,
"fromAmount" : 100000 ,
"toAmount" : 23.81 ,
"isPreFunded" : false ,
"active" : true ,
"createdAt" : "2025-01-15T23:46:10.226Z" ,
"updatedAt" : "2025-01-15T23:46:10.226Z"
},
"createdAt" : "2025-01-15T23:46:10.226Z" ,
"updatedAt" : "2025-01-15T23:46:10.226Z" ,
"attempts" : 0
}
Exemplo - Ramp concluído:
{
"id" : "evt_b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e" ,
"event" : "RAMP" ,
"action" : "UPDATE" ,
"data" : {
"id" : "be4d353b-00a2-4309-9ef1-594f37dfb1fd" ,
"userId" : "2bc703f2-1c54-4cbb-a144-993e1d957688" ,
"accountId" : "f7df00af-5d12-4b33-b5ab-e2b32290ebad" ,
"type" : "ON" ,
"status" : "COMPLETED" ,
"fromCurrency" : "COP" ,
"toCurrency" : "USDC" ,
"fromAmount" : 100000 ,
"toAmount" : 23.81 ,
"transferProof" : "0x8e2b1f4645ebc8b6f658c860980567a63d283a42735be7401a7950306a2539b0" ,
"createdAt" : "2025-01-15T23:46:10.226Z" ,
"updatedAt" : "2025-01-16T00:29:06.813Z"
},
"createdAt" : "2025-01-16T00:29:06.813Z" ,
"updatedAt" : "2025-01-16T00:29:06.813Z" ,
"attempts" : 0
}
Como lidar com eventos RAMP:
if ( webhookEvent . event === 'RAMP' && webhookEvent . action === 'UPDATE' ) {
const { status , transferProof } = webhookEvent . data ;
switch ( status ) {
case 'CASH_IN_COMPLETED' :
console . log ( 'Payment received' );
break ;
case 'COMPLETED' :
console . log ( 'Ramp completed:' , transferProof );
await notifyUser ( webhookEvent . data . userId );
break ;
case 'FAILED' :
console . log ( 'Ramp failed' );
await handleFailure ( webhookEvent . data );
break ;
}
}
Status comuns:
CREATED - Ramp inicializado
CASH_IN_PROCESSING - Pagamento sendo processado
CASH_IN_COMPLETED - Pagamento confirmado
CONVERSION_PROCESSING - Convertendo moeda
CONVERSION_COMPLETED - Conversão concluída
CASH_OUT_PROCESSING - Enviando fundos
COMPLETED - Totalmente concluído
FAILED - Falhou
CANCELED - Cancelado
Eventos USER
Tipo: USER
Ações:
CREATE - Usuário registrado
UPDATE - Informações ou status do usuário mudou
DELETE - Usuário excluído (raro)
Exemplo - KYC aprovado:
{
"id" : "evt_f1e2d3c4-b5a6-4978-8c9d-0e1f2a3b4c5d" ,
"event" : "USER" ,
"action" : "UPDATE" ,
"data" : {
"id" : "e3d5c4ca-839a-4067-af76-89b33b19696e" ,
"type" : "PERSON" ,
"customerId" : "c650925e-a4aa-4a5f-a0a9-6c97be6c6ce5" ,
"externalId" : "user-ext-12345" ,
"status" : "ACTIVE" ,
"accessLevel" : "L2" ,
"data" : {
"firstName" : "Carlos" ,
"lastName" : "Rodriguez" ,
"email" : "[email protected] " ,
"phone" : "+573001234567"
}
},
"createdAt" : "2025-01-15T10:30:00.000Z" ,
"updatedAt" : "2025-01-15T14:22:00.000Z" ,
"attempts" : 0
}
Como lidar com eventos USER:
if ( webhookEvent . event === 'USER' && webhookEvent . action === 'UPDATE' ) {
const { status , accessLevel } = webhookEvent . data ;
if ( status === 'ACTIVE' && accessLevel !== 'L0' ) {
// KYC aprovado - habilitar funcionalidades
await enableUserFeatures ( webhookEvent . data . id , accessLevel );
}
if ( status === 'REJECTED' ) {
// KYC rejeitado - notificar o usuário
await sendKYCRejectionEmail ( webhookEvent . data );
}
}
Status do usuário:
PENDING - Aguardando verificação
ACTIVE - Verificado e ativo
REJECTED - KYC rejeitado
SUSPENDED - Conta suspensa
Níveis de acesso:
L0 - Não verificado
L1 - Básico ($1K diário)
L2 - Padrão ($10K diário)
L3 - Aprimorado ($50K diário)
L4 - Premium (Ilimitado)
Eventos ACCOUNT
Tipo: ACCOUNT
Ações:
CREATE - Conta adicionada
UPDATE - Status da conta mudou
DELETE - Conta excluída
Exemplo - Conta verificada:
{
"id" : "evt_c9b8a7f6-d5e4-4321-9876-543210fedcba" ,
"event" : "ACCOUNT" ,
"action" : "UPDATE" ,
"data" : {
"id" : "543ab81d-0b1e-4b9d-88bc-58ba5a365f16" ,
"type" : "PSE" ,
"status" : "ACTIVE" ,
"externalId" : "bank-acc-7890" ,
"data" : {
"firstName" : "María" ,
"lastName" : "González" ,
"email" : "[email protected] " ,
"countryCode" : "CO"
},
"complianceUrl" : "https://in.sumsub.com/websdk/p/AbCd1234EfGh5678"
},
"createdAt" : "2025-01-15T09:15:00.000Z" ,
"updatedAt" : "2025-01-15T09:45:00.000Z" ,
"attempts" : 0
}
Como lidar com eventos ACCOUNT:
if ( webhookEvent . event === 'ACCOUNT' && webhookEvent . action === 'UPDATE' ) {
const { status , type } = webhookEvent . data ;
if ( status === 'ACTIVE' ) {
console . log ( ` ${ type } account verified and ready` );
await notifyAccountReady ( webhookEvent . data . id );
}
if ( status === 'REJECTED' ) {
console . log ( 'Account verification failed' );
await requestNewAccountInfo ( webhookEvent . data . id );
}
}
Tipos de conta:
PSE - Banco colombiano
SPEI - Banco mexicano
ACH - Banco nos EUA
WIRE - Transferência bancária
WALLET - Carteira cripto
PIX - PIX do Brasil
TRANSFIYA - Carteira móvel na Colômbia
Status de conta:
PENDING - Aguardando verificação
ACTIVE - Verificada
REJECTED - Verificação falhou
INACTIVE - Desativada
Eventos TRANSACTION
Tipo: TRANSACTION
Ações:
CREATE - Transação iniciada
UPDATE - Status da transação mudou
DELETE - Transação cancelada (raro)
Exemplo - Depósito em savings:
{
"id" : "evt_1a2b3c4d-5e6f-7890-abcd-ef1234567890" ,
"event" : "TRANSACTION" ,
"action" : "UPDATE" ,
"data" : {
"id" : "txn_9876543210abcdef" ,
"userId" : "e3d5c4ca-839a-4067-af76-89b33b19696e" ,
"savingsAccountId" : "sav_1234567890abcdef" ,
"type" : "DEPOSIT" ,
"status" : "COMPLETED" ,
"amount" : "1000.00" ,
"currency" : "USD" ,
"method" : "ACH"
},
"createdAt" : "2025-01-15T10:30:00.000Z" ,
"updatedAt" : "2025-01-15T10:35:00.000Z" ,
"attempts" : 0
}
Tipos de transação:
DEPOSIT - Depósito em savings
WITHDRAWAL - Saque de savings
INTEREST - Pagamento de juros
FEE - Cobrança de taxa
Eventos CUSTODIAL_ACCOUNT
Tipo: CUSTODIAL_ACCOUNT
Ações:
CREATE - Conta criada
UPDATE - Saldo ou status atualizado
DELETE - Conta encerrada (raro)
Exemplo - Saldo atualizado:
{
"id" : "evt_abcd1234-ef56-7890-1234-567890abcdef" ,
"event" : "CUSTODIAL_ACCOUNT" ,
"action" : "UPDATE" ,
"data" : {
"id" : "cust_1234567890abcdef" ,
"userId" : "e3d5c4ca-839a-4067-af76-89b33b19696e" ,
"type" : "SAVINGS" ,
"status" : "ACTIVE" ,
"balance" : "5250.00" ,
"currency" : "USD" ,
"previousBalance" : "5000.00" ,
"changeAmount" : "250.00" ,
"changeReason" : "DEPOSIT" ,
"interestRate" : "4.50"
},
"createdAt" : "2025-01-15T10:35:00.000Z" ,
"updatedAt" : "2025-01-15T10:35:00.000Z" ,
"attempts" : 0
}
Motivos de mudança:
DEPOSIT - Fundos depositados
WITHDRAWAL - Fundos sacados
INTEREST - Juros creditados
FEE - Taxa cobrada
ADJUSTMENT - Ajuste manual
Boas práticas de tratamento de eventos
Filtrar por evento e ação
const handleWebhook = ( webhookEvent ) => {
const { event , action , data } = webhookEvent ;
// Roteie por tipo de evento
if ( event === 'RAMP' ) {
if ( action === 'CREATE' ) {
handleRampCreated ( data );
} else if ( action === 'UPDATE' ) {
handleRampUpdated ( data );
}
} else if ( event === 'USER' ) {
if ( action === 'UPDATE' ) {
handleUserUpdated ( data );
}
}
// ... outros tipos de eventos
};
Verificar status específico
const handleRampUpdated = ( data ) => {
switch ( data . status ) {
case 'CASH_IN_COMPLETED' :
console . log ( 'Payment received' );
break ;
case 'COMPLETED' :
console . log ( 'Ramp completed:' , data . transferProof );
updateDatabase ( data );
notifyUser ( data . userId );
break ;
case 'FAILED' :
console . log ( 'Ramp failed:' , data . details );
handleFailure ( data );
break ;
}
};
Usar o ID do evento para idempotência
const processWebhook = async ( webhookEvent ) => {
// Verifique se já foi processado
const existing = await db . webhookEvents . findOne ({
eventId: webhookEvent . id
});
if ( existing ) {
console . log ( 'Duplicate event, skipping' );
return { received: true , duplicate: true };
}
// Armazenar e processar
await db . webhookEvents . create ({
eventId: webhookEvent . id ,
eventType: webhookEvent . event ,
action: webhookEvent . action ,
payload: webhookEvent . data ,
attempts: webhookEvent . attempts ,
receivedAt: new Date ()
});
await handleWebhook ( webhookEvent );
return { received: true };
};
Monitorar tentativas de retry
if ( webhookEvent . attempts > 3 ) {
console . warn ( `High retry count for event ${ webhookEvent . id } ` );
// Alertar sistema de monitoramento
alertOps ( 'webhook-retries-high' , {
eventId: webhookEvent . id ,
attempts: webhookEvent . attempts ,
event: webhookEvent . event
});
}
Referência rápida
Tipo de evento Ações comuns Campos chave em data RAMP CREATE, UPDATE status, transferProof, fromAmount, toAmount USER CREATE, UPDATE status, accessLevel, data ACCOUNT CREATE, UPDATE, DELETE status, type, data TRANSACTION CREATE, UPDATE type, status, amount CUSTODIAL_ACCOUNT UPDATE balance, changeReason, changeAmount
Próximos passos
Configuração de Webhook Configure endpoints de webhook
Guia de Segurança Verifique assinaturas de webhook
Conceito de Webhook Aprenda sobre a arquitetura de webhooks
Referência da API Documentação da API de Webhooks