The IBEx.Fi API provides WebSocket connections to receive real-time updates for wallet balances, transactions, and user data. This eliminates the need for constant polling and provides instant notifications when blockchain events occur.
Connect to the WebSocket endpoint using the standard WebSocket protocol.
Endpoint : ws://host/ws or wss://host/ws (secure)
Connection Example :
const ws = new WebSocket('wss://api.ibex.fi/ws');
ws.onopen = () => {
console.log('✅ WebSocket connected');
// Authentication will be sent next
};
wss:// (secure WebSocket) in production. The WebSocket connection is established first, then authentication happens via a JSON message.
After the WebSocket connection is established, authenticate using a JWT token (the same token used for HTTP API calls).
Authentication Message (Client → Server) :
{
"type": "auth",
"token": "eyJhbGciOi...", // JWT token from POST /v1.2/auth/sign-in
"clientName": "My App" // Optional: useful for monitoring
}
JavaScript Example :
ws.onopen = () => {
// Send authentication immediately after connection opens
ws.send(JSON.stringify({
type: 'auth',
token: accessToken, // Your JWT access token
clientName: 'My Wallet App'
}));
};
Authentication Success Response (Server → Client) :
{
"type": "auth_success",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"message": "Connected to real-time updates"
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Authentication Error Response (Server → Client) :
{
"type": "auth_error",
"data": {
"message": "JWT token expired, please refresh your token",
"error_code": "TOKEN_EXPIRED",
"context": "auth_process"
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
rpId from the hostname and verifies that the JWT iss matches rpIdauth_error message with detailsImmediately after successful authentication, the server automatically sends the following data without any additional requests:
Message Type : balance_data
{
"type": "balance_data",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"balance": {
"balances": [
{
"token": "0xEURE12345678901234567890123456789012345678",
"symbol": "EURE",
"balance": "123.456789",
"decimals": 18
}
]
}
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Message Type : transaction_data
{
"type": "transaction_data",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"transactions": {
"data": [
{
"hash": "0xabcdef1234567890...",
"blockNumber": 12345678,
"timestamp": "2025-01-31T11:59:00.000Z",
"from": "0xabc...",
"to": "0x1234...",
"value": "100.0",
"direction": "IN",
"tokenSymbol": "EURE"
}
]
}
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Message Type : chainid_data
{
"type": "chainid_data",
"data": {
"defaultChainId": 421614,
"supportedChainIds": [421614]
},
"timestamp": "2025-01-31T12:00:01.000Z"
}
Message Type : recovery_data
{
"type": "recovery_data",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"recoveryEnabled": false,
"recoveryAddress": null,
"delay": null,
"pendingRecovery": false,
"canExecute": false,
"executeAfter": null
},
"timestamp": "2025-01-31T12:00:01.000Z"
}
Message Type : user_data
{
"type": "user_data",
"data": {
"id": "ext_user_123",
"ky": "5",
"signers": [
{
"id": "signer_id",
"safes": [
{
"address": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"threshold": 1,
"iban": {
"chainId": 421614,
"iban": "FR76...",
"bic": "CREDFRPP"
}
}
]
}
]
},
"timestamp": "2025-01-31T12:00:01.000Z"
}
After authentication, you automatically receive real-time updates for your Safe address. No additional subscription is required.
Message Type : balance_update
Sent automatically when the wallet balance changes (new transactions, token transfers, etc.).
{
"type": "balance_update",
"data": {
"address": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"balance": "123.456789",
"updated_at": "2025-01-31T12:00:00.000Z"
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Message Type : new_transaction
Sent automatically when a new transaction is detected for your Safe address.
{
"type": "new_transaction",
"data": {
"address": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"newTransaction": {
"hash": "0xabcdef1234567890...",
"blockNumber": 12345678,
"timestamp": "2025-01-31T12:00:00.000Z",
"from": "0xabc...",
"to": "0xdef...",
"tokenAddress": "0xEURE12345678901234567890123456789012345678",
"tokenType": "ERC20",
"tokenSymbol": "EURE",
"tokenName": "Monerium EUR emoney",
"value": "100.0",
"direction": "IN"
},
"recentTransactions": [ /* recent transactions (optional) */ ],
"transactionCount": 1,
"historyLimit": 5
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Message Type : user.iban.updated
Sent automatically when the IBAN status changes (e.g., from PENDING to VERIFIED).
{
"type": "user.iban.updated",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"iban": "DE89 3704 0044 0532 0130 00",
"previousState": "PENDING",
"newState": "VERIFIED",
"updatedAt": "2025-08-01T07:36:04.083Z"
},
"timestamp": "2025-08-01T07:36:04.083Z"
}
Message Type : user.ky.updated
Sent automatically when the KYC status changes (e.g., from PENDING to VERIFIED).
{
"type": "user.ky.updated",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"previousKyc": "PENDING",
"newKyc": "VERIFIED",
"updatedAt": "2025-08-01T07:36:04.083Z"
},
"timestamp": "2025-08-01T07:36:04.083Z"
}
You can also request balance or transaction data on demand by sending specific message types.
Message Type (Client → Server) : get_balance
{
"type": "get_balance",
"requestId": "req-123" // Optional: for tracking responses
}
Response (Server → Client) :
{
"type": "balance_data",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"balance": { /* balance data */ },
"requestId": "req-123" // Echoed if provided
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
Message Type (Client → Server) : get_transactions
{
"type": "get_transactions",
"params": {
"startDate": "2025-01-01", // Optional
"endDate": "2025-01-31", // Optional
"limit": 50, // Optional
"page": 1 // Optional
},
"requestId": "req-456" // Optional: for tracking responses
}
Response (Server → Client) :
{
"type": "transaction_data",
"data": {
"safeAddress": "0xd676c6188195372EC269E9C2cAf815C56436A679",
"transactions": { /* transaction data */ },
"requestId": "req-456" // Echoed if provided
},
"timestamp": "2025-01-31T12:00:00.000Z"
}
class IBEXWebSocket {
constructor(apiUrl, jwtToken) {
this.apiUrl = apiUrl;
this.jwtToken = jwtToken;
this.ws = null;
this.reconnectInterval = null;
this.maxReconnectAttempts = 5;
this.reconnectAttempts = 0;
this.isAuthenticated = false;
}
connect() {
try {
console.log('🔌 Connecting to IBEX WebSocket...');
this.ws = new WebSocket(this.apiUrl);
this.ws.onopen = () => {
console.log('✅ WebSocket connected');
this.reconnectAttempts = 0;
// Send authentication immediately
this.ws.send(JSON.stringify({
type: 'auth',
token: this.jwtToken,
clientName: 'My Wallet App'
}));
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
this.ws.onclose = (event) => {
console.log(`👋 Connection closed: ${event.code} ${event.reason}`);
this.isAuthenticated = false;
this.scheduleReconnect();
};
this.ws.onerror = (error) => {
console.error('❌ WebSocket error:', error);
};
} catch (error) {
console.error('❌ Connection failed:', error);
this.scheduleReconnect();
}
}
handleMessage(message) {
console.log('📥 Received:', message.type);
switch(message.type) {
case 'auth_success':
console.log('🔐 Authenticated, Safe:', message.data.safeAddress);
this.isAuthenticated = true;
this.onAuthSuccess(message.data);
break;
case 'auth_error':
console.error('❌ Authentication failed:', message.data.message);
this.onAuthError(message.data);
break;
case 'balance_data':
console.log('💰 Balance data:', message.data.balance);
this.onBalanceData(message.data);
break;
case 'balance_update':
console.log('💰 Balance updated:', message.data.balance);
this.onBalanceUpdate(message.data);
break;
case 'transaction_data':
console.log('📊 Transaction data:', message.data.transactions);
this.onTransactionData(message.data);
break;
case 'new_transaction':
console.log('🔄 New transaction:', message.data.newTransaction.hash);
this.onNewTransaction(message.data);
break;
case 'chainid_data':
console.log('⛓️ Chain IDs:', message.data);
this.onChainIdData(message.data);
break;
case 'recovery_data':
console.log('🔒 Recovery status:', message.data);
this.onRecoveryData(message.data);
break;
case 'user_data':
console.log('👤 User data:', message.data);
this.onUserData(message.data);
break;
case 'user.iban.updated':
console.log('🏦 IBAN updated:', message.data);
this.onIbanUpdated(message.data);
break;
case 'user.ky.updated':
console.log('✅ KYC updated:', message.data);
this.onKycUpdated(message.data);
break;
case 'error':
console.error('❌ Error:', message.data);
this.onError(message.data);
break;
default:
console.log('📝 Unknown message type:', message.type);
}
}
// Request balance on demand
requestBalance() {
if (this.isAuthenticated && this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'get_balance',
requestId: `balance-${Date.now()}`
}));
}
}
// Request transactions on demand
requestTransactions(params = {}) {
if (this.isAuthenticated && this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'get_transactions',
params: {
startDate: params.startDate || '2025-01-01',
endDate: params.endDate || new Date().toISOString().split('T')[0],
limit: params.limit || 50,
page: params.page || 1
},
requestId: `transactions-${Date.now()}`
}));
}
}
scheduleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('❌ Max reconnect attempts reached');
return;
}
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
this.reconnectAttempts++;
console.log(`🔄 Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
this.reconnectInterval = setTimeout(() => {
this.connect();
}, delay);
}
// Override these methods in your implementation
onAuthSuccess(data) {
// Handle successful authentication
}
onAuthError(data) {
// Handle authentication error (e.g., refresh token)
}
onBalanceData(data) {
// Handle initial balance data
}
onBalanceUpdate(data) {
// Handle balance updates
}
onTransactionData(data) {
// Handle initial transaction data
}
onNewTransaction(data) {
// Handle new transaction notifications
}
onChainIdData(data) {
// Handle chain ID data
}
onRecoveryData(data) {
// Handle recovery status
}
onUserData(data) {
// Handle user data
}
onIbanUpdated(data) {
// Handle IBAN status updates
}
onKycUpdated(data) {
// Handle KYC status updates
}
onError(data) {
// Handle errors
}
disconnect() {
if (this.reconnectInterval) {
clearTimeout(this.reconnectInterval);
}
if (this.ws) {
this.ws.close();
}
}
}
// Usage
const ws = new IBEXWebSocket('wss://api.ibex.fi/ws', 'your-jwt-token');
ws.connect();
// Request data on demand
ws.requestBalance();
ws.requestTransactions({ limit: 20 });
TOKEN_EXPIRED, refresh your token and reconnectThe server automatically sends a ping every 30 seconds to keep the connection alive. Standard WebSocket clients automatically respond with a pong — no client action is required.
This section provides detailed technical information for AI systems integrating the IBEX WebSocket flow, including architecture patterns, message formats, and implementation details.
Connection Flow :
/wsClient → Server Messages :
auth : Authenticate with JWT tokenget_balance : Request current balanceget_transactions : Request transaction historyServer → Client Messages :
auth_success : Authentication successfulauth_error : Authentication failedbalance_data : Balance data (initial or on-demand)balance_update : Real-time balance updatetransaction_data : Transaction data (initial or on-demand)new_transaction : Real-time new transaction notificationchainid_data : Chain ID informationrecovery_data : Recovery statususer_data : User informationuser.iban.updated : IBAN status changeuser.ky.updated : KYC status changeerror : Error messagesrc/routes/websocket.ts : WebSocket route handler and authenticationsrc/services/WebSocketManager.ts : Connection management and message broadcastingsrc/services/webSocketSubscriptionService.ts : BCReader WebSocket subscription servicedocs/IBEXFIAPI_WEBSOCKET_GUIDE.md : Complete WebSocket documentationwss://api.ibex.fi/ws{ type: 'auth', token: '...', clientName: '...' }rpId and externalUserIdbalance_data, transaction_data, chainid_data, recovery_data, user_databalance_update and new_transaction messages from BCReaderuser.iban.updated and user.ky.updated when status changesget_balance or get_transactions at any time