Event Structure
All webhook events follow this structure:
{
id: string, // UUID - unique event ID
event: string, // Event type: RAMP | USER | TRANSACTION | ACCOUNT | CUSTODIAL_ACCOUNT
action: string, // Action: CREATE | UPDATE | DELETE
data: object, // Event-specific data
createdAt: string, // ISO 8601 timestamp
updatedAt: string, // ISO 8601 timestamp
attempts: number // Delivery attempt count (0 to 5)
}
Use the id field for idempotency. The attempts field indicates retry count.
Event Types
RAMP Events
Type: RAMP
Actions:
CREATE - Ramp created
UPDATE - Ramp status changed
DELETE - Ramp canceled (rare)
Example - Ramp Created:
{
"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
}
Example - Ramp Completed:
{
"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
}
Handling Ramp Events:
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;
}
}
Common Statuses:
CREATED - Ramp initialized
CASH_IN_PROCESSING - Payment being processed
CASH_IN_COMPLETED - Payment confirmed
CONVERSION_PROCESSING - Converting currency
CONVERSION_COMPLETED - Conversion done
CASH_OUT_PROCESSING - Sending funds
COMPLETED - Fully completed
FAILED - Failed
CANCELED - Canceled
USER Events
Type: USER
Actions:
CREATE - User registered
UPDATE - User information or status changed
DELETE - User deleted (rare)
Example - KYC Approved:
{
"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
}
Handling User Events:
if (webhookEvent.event === 'USER' && webhookEvent.action === 'UPDATE') {
const { status, accessLevel } = webhookEvent.data;
if (status === 'ACTIVE' && accessLevel !== 'L0') {
// KYC approved - enable features
await enableUserFeatures(webhookEvent.data.id, accessLevel);
}
if (status === 'REJECTED') {
// KYC rejected - notify user
await sendKYCRejectionEmail(webhookEvent.data);
}
}
User Statuses:
PENDING - Awaiting verification
ACTIVE - Verified and active
REJECTED - KYC rejected
SUSPENDED - Account suspended
Access Levels:
L0 - Unverified
L1 - Basic ($1K daily)
L2 - Standard ($10K daily)
L3 - Enhanced ($50K daily)
L4 - Premium (Unlimited)
ACCOUNT Events
Type: ACCOUNT
Actions:
CREATE - Account added
UPDATE - Account status changed
DELETE - Account deleted
Example - Account Verified:
{
"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
}
Handling Account Events:
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);
}
}
Account Types:
PSE - Colombian bank
SPEI - Mexican bank
ACH - US bank
WIRE - Wire transfer
WALLET - Crypto wallet
PIX - Brazilian PIX
TRANSFIYA - Colombian mobile wallet
Account Statuses:
PENDING - Awaiting verification
ACTIVE - Verified
REJECTED - Verification failed
INACTIVE - Disabled
TRANSACTION Events
Type: TRANSACTION
Actions:
CREATE - Transaction initiated
UPDATE - Transaction status changed
DELETE - Transaction canceled (rare)
Example - Savings Deposit:
{
"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
}
Transaction Types:
DEPOSIT - Deposit to savings
WITHDRAWAL - Withdrawal from savings
INTEREST - Interest payment
FEE - Fee charge
CUSTODIAL_ACCOUNT Events
Type: CUSTODIAL_ACCOUNT
Actions:
CREATE - Account created
UPDATE - Balance or status updated
DELETE - Account closed (rare)
Example - Balance Updated:
{
"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
}
Change Reasons:
DEPOSIT - Funds deposited
WITHDRAWAL - Funds withdrawn
INTEREST - Interest credited
FEE - Fee charged
ADJUSTMENT - Manual adjustment
Event Handling Best Practices
Filter by Event and Action
const handleWebhook = (webhookEvent) => {
const { event, action, data } = webhookEvent;
// Route by event type
if (event === 'RAMP') {
if (action === 'CREATE') {
handleRampCreated(data);
} else if (action === 'UPDATE') {
handleRampUpdated(data);
}
} else if (event === 'USER') {
if (action === 'UPDATE') {
handleUserUpdated(data);
}
}
// ... other event types
};
Check Specific Status
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;
}
};
Use Event ID for Idempotency
const processWebhook = async (webhookEvent) => {
// Check if already processed
const existing = await db.webhookEvents.findOne({
eventId: webhookEvent.id
});
if (existing) {
console.log('Duplicate event, skipping');
return { received: true, duplicate: true };
}
// Store and process
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 };
};
Monitor Retry Attempts
if (webhookEvent.attempts > 3) {
console.warn(`High retry count for event ${webhookEvent.id}`);
// Alert monitoring system
alertOps('webhook-retries-high', {
eventId: webhookEvent.id,
attempts: webhookEvent.attempts,
event: webhookEvent.event
});
}
Quick Reference
| Event Type | Common Actions | Key Data Fields |
|---|
| 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 |
Next Steps