Why Use Webhooks

When integrating with HIFI's stablecoin infrastructure, it's essential to have your applications respond to events as they occur within your HIFI accounts. This enables your backend systems to take appropriate actions promptly.

To receive webhook events, you need to register webhook endpoint . Once registered, HIFI can push real-time event data to your application's webhook endpoint whenever an event occurs in your HIFI account. HIFI uses HTTPS to deliver webhook events as a JSON payload containing an Event object.

Receiving webhook events is particularly valuable for monitoring asynchronous events such as when a transaction is confirmed or a user object is updated.

Event Overview

By registering webhook endpoints in your HIFI account, you enable HIFI to automatically send Event objects as part of POST requests to the registered webhook endpoint hosted by your application. After your webhook endpoint receives the Event, your app can execute backend actions (for example, updating your transaction records or notifying users after receiving a TRANSFER.CRYPTO_TO_CRYPTO event).

{
  "eventId": "89c88a4e-803d-4eed-94aa-a66bd9441ebc",
  "eventType": "TRANSFER.CRYPTO_TO_FIAT",
  "eventAction": "UPDATE",
  "timestamp": "2024-06-29T20:03:58.735033+00:00",
  "data": {
    "transferType": "CRYPTO_TO_CRYPTO",
    "transferDetails": {
        "id": "ca7ee175-66ca-4b43-a70a-9ac21450820b",
        "requestId": "9bdf2251-0b96-41ce-bb33-2e3a29fb6035",
        "senderUserId": "75d7c01f-5f93-4490-8b93-a62fd8020358",
        "recipientUserId": null,
        "recipientAddress": "0xFb674069Bd8CfB2a63ACCe3b58E601FB9f914665",
        "chain": "POLYGON_MAINNET",
        "currency": "usdc",
        "transactionHash": "0x91ebabf2867d7b28532c8ac9bc9e37f442c121486583d659aef9531fa97eacc0",
        "createdAt": "2024-08-12T18:15:01.39654+00:00",
        "updatedAt": "2024-08-12T18:16:20.833+00:00",
        "status": "CONFIRMED",
        "contractAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
        "failedReason": null,
        "sender": {
            "business_name": null,
            "legal_last_name": "YANG",
            "compliance_email": "[email protected]",
            "legal_first_name": "William"
        },
        "fee": {
            "feeId": "15698c9b-6761-42ef-8c0c-96f5f983ba7d",
            "feeType": "FIX",
            "feeAmount": 0.01,
            "feePercent": 0,
            "status": "CONFIRMED",
            "transactionHash": "0x91ebabf2867d7b28532c8ac9bc9e37f442c121486583d659aef9531fa97eacc0",
            "failedReason": null
        }
    }
	}
}

Event type

You receive events for all the event types your webhook endpoint in your HIFI account. Use the received eventType and eventAction to determine what processing your application needs to perform. The data.object corresponding to each event type varies.

Event types HIFI will send:

  • USER
    • STATUS
  • TRANSFER
    • CRYPTO_TO_CRYPTO
    • CRYPTO_TO_FIAT
    • FIAT_TO_CRYPTO

Event actions HIFI will send:

  • UPDATE

Retry Behavior

HIFI will try to re-deliver a given event to your webhook endpoint for up to 3 days with an exponential backoff when failed to receive 2xx status from the webhook endpoint.

Retry interval: 60 seconds to an hour based on the number of retries.

If your endpoint has been disabled or deleted when HIFI attempts a retry, future retries of that event are prevented. However, if you disable and then re-enable a webhook endpoint before HIFI can retry, you can still expect to see future retry attempts.

Verify Data Integrity and Ensure Events Are Sent from HIFI

After activating your webhook, you will receive a webhook secret. We use this secret to sign the payload using JWT, and the signature is included in the Authorization of the request header. You will need to verify this signature to ensure the authenticity of the event. By validating the JWT signature with your webhook secret, you can confirm that the event was sent by HIFI and that the data has not been tampered with during transmission. This verification step is crucial for maintaining the security and integrity of your webhook integration.

# Replace \n if needed 
WEBHOOK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...........\n-----END PUBLIC KEY-----\n"
from flask import Flask, request, jsonify
import jwt

app = Flask(__name__)
port = 3000  # Replace with your desired port number
webhook_public_key = '''your_webhook_public_key'''  # Replace with your actual webhook 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 decoded payload as needed
        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

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

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();
const port = 3000; // Replace with your desired port number
const webhookPublicKey = 'your_webhook_public_key'; // Replace with your actual webhook secret

// Middleware to parse JSON bodies
app.use(bodyParser.json());

// Endpoint to handle incoming webhook POST requests
app.post('/webhook', (req, res) => {
    const authHeader = req.headers['authorization'];
    if (!authHeader) {
        return res.status(401).send('Authorization header is missing');
    }

    const jwtToken = authHeader.split(' ')[1];

    try {
        jwt.verify(token, webhookPublicKey.replace(/\\n/g, '\n'), { algorithms: ['RS256'] },(err, decoded) => {
            if (err) {
                console.error('Failed to verify token:', err.message);
                throw new Error("wrong token")
            } else {
                console.log('Token is valid. Decoded payload:', decoded);
            }
        });
        // Process the decoded payload as needed
        res.sendStatus(200);
    } catch (err) {
        console.error('Token verification failed:', err.message);
        res.status(401).send('Token verification failed');
    }
});

// Start the server
app.listen(port, () => {
    console.log(Server is running on http://localhost:${port});
});