Skip to main content
Webhooks deliver real-time event notifications from HIFI to your application. When events occur in your HIFI account, HIFI sends HTTP POST requests to your registered webhook endpoints, enabling your systems to respond immediately.

How Webhooks Work

1

Register endpoint

Configure a webhook endpoint URL in the HIFI Dashboard.
2

Receive events

HIFI sends POST requests to your endpoint when events occur.
3

Verify signature

Validate the JWT signature using your webhook secret to ensure authenticity.
4

Process event

Execute your application logic based on the event data.
5

Return 2xx response

Respond with a 2xx status code to acknowledge receipt.

Registering Webhooks

Register webhook endpoints in the HIFI Dashboard:
  1. Navigate to Developer → Webhooks
  2. Enter your webhook URL (must be HTTPS)
  3. Save to receive your webhook secret
Webhook Secret: After registration, you’ll receive a public key used to verify webhook signatures. Store this securely - you’ll need it to validate incoming requests.

Event Structure

All webhook events follow a consistent structure:
{
  "eventId": "evt_1957117404034e3ade",
  "eventCategory": "USER",
  "eventType": "USER.CREATE",
  "eventAction": "CREATE",
  "data": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "type": "individual",
    "wallets": {
      "INDIVIDUAL": {
        "POLYGON": {
          "address": "0xa8A642FBA80749318036C97344fC73aE0B64c608"
        }
      }
    }
  },
  "createdAt": "2025-03-07T14:51:44.099Z",
  "timestamp": "2025-03-07T14:52:00.375Z",
  "version": "v2"
}
Event Fields:
eventId
string
Unique identifier for this webhook event.
eventCategory
string
Broad category describing the domain of the event (e.g., USER, WALLET, ONRAMP, OFFRAMP).
eventType
string
Specific event type or trigger (such as USER.CREATE, WALLET.TRANSFER.UPDATE).
eventAction
string
Operation performed (CREATE, UPDATE, DELETE).
data
object
Object containing details and payload for this event (structure varies by event type).
createdAt
string
ISO timestamp when the event was generated by HIFI.
timestamp
string
ISO timestamp when the webhook was delivered.
version
string
Version of the webhook/event payload schema (e.g., v2).

Event Types

HIFI sends webhook events for various types of operations. Each event category has a dedicated page with detailed information about the event types, payloads, and examples.
  • User Events - Events for user creation and updates
  • KYC Events - Events for KYC data and status changes
  • Wallet Events - Events for wallet transfers, bridges, and token balance updates
  • Account Events - Events for onramp, offramp, and virtual account creation and updates
  • Onramp Events - Events for onramp transfer creation and status updates
  • Offramp Events - Events for offramp transfer creation and status updates

Verifying Webhook Signatures

Critical: Always verify webhook signatures to ensure requests are from HIFI and haven’t been tampered with.

Verification Process

Webhooks include a JWT token in the Authorization header. Verify this token using your webhook public key:
  1. Extract the JWT token from the Authorization: Bearer <token> header
  2. Verify the token using your webhook public key with RS256 algorithm
  3. If verification succeeds, process the event
  4. If verification fails, reject the request with 401 status

Implementation Examples

from flask import Flask, request, jsonify
import jwt

app = Flask(__name__)
webhook_public_key = '''-----BEGIN PUBLIC KEY-----
your_webhook_public_key_here
-----END PUBLIC KEY-----'''

@app.route('/webhook', methods=['POST'])
def webhook():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return 'Authorization header is missing', 401

    jwt_token = auth_header.split(' ')[1]

    try:
        decoded = jwt.decode(jwt_token, webhook_public_key, algorithms=['RS256'])
        print('Token is valid. Decoded payload:', decoded)
        
        # Process the event
        event_type = decoded.get('eventType')
        event_data = decoded.get('data')
        
        # Your application logic here
        handle_event(event_type, event_data)
        
        return '', 200
    except jwt.ExpiredSignatureError:
        print('Token has expired')
        return 'Token has expired', 401
    except jwt.InvalidTokenError as e:
        print('Failed to verify token:', str(e))
        return 'Token verification failed', 401

def handle_event(event_type, data):
    if event_type == 'WALLET.TRANSFER.UPDATE':
        # Update transaction status in your database
        pass
    elif event_type == 'USER.KYC.UPDATE':
        # Notify user of KYC status change
        pass

if __name__ == '__main__':
    app.run(port=3000)

Retry Behavior

HIFI automatically retries failed webhook deliveries to ensure reliability.

Retry Schedule

  • Duration: Up to 24 hours
  • Strategy: Exponential backoff
  • Interval: 60 seconds to 1 hour between retries
  • Success criteria: Your endpoint returns a 2xx status code

Retry Conditions

HIFI retries when:
  • Your endpoint returns a 5xx error
  • Connection timeout occurs
  • Network error prevents delivery
HIFI does not retry when:
  • Your endpoint returns a 4xx error (indicates a permanent failure)
  • Your webhook endpoint is disabled or deleted
  • 24 hours have elapsed since the original event
Endpoint availability matters: If your endpoint is disabled when HIFI attempts a retry, future retries for that event are prevented. Re-enable your endpoint before the next retry attempt to continue receiving retries.

Best Practices

Return a 2xx response as soon as you receive the webhook, before processing the event. Process the event asynchronously to avoid timeouts:
app.post('/webhook', (req, res) => {
  // Verify signature
  const event = verifyAndExtract(req);
  
  // Respond immediately
  res.status(200).send('');
  
  // Process asynchronously
  processEventAsync(event);
});
Use eventId to ensure you process each event only once. Store processed event IDs:
async function handleEvent(event) {
  // Check if already processed
  const processed = await db.hasProcessed(event.eventId);
  if (processed) {
    console.log('Event already processed:', event.eventId);
    return;
  }
  
  // Process event
  await processEvent(event);
  
  // Mark as processed
  await db.markProcessed(event.eventId);
}
Events may arrive out of order. Use createdAt and timestamp to determine the correct sequence. Store the latest known state:
function shouldProcessEvent(event, currentState) {
  const eventTime = new Date(event.createdAt);
  const lastUpdate = new Date(currentState.lastUpdatedAt);
  
  // Only process if this event is newer
  return eventTime > lastUpdate;
}
Track webhook delivery success rates and response times. Set up alerts for:
  • High failure rates
  • Slow response times
  • Signature verification failures
This helps identify issues early before retries are exhausted.
Use the HIFI sandbox environment to test your webhook integration:
  1. Register a test webhook endpoint
  2. Trigger test events using sandbox API
  3. Verify your endpoint handles events correctly
  4. Test retry behavior by temporarily failing your endpoint

Getting Help