Notifications¶
Overview¶
The Notification service provides real-time WebSocket connections for receiving notifications about events in your workspace. Get instant updates about data changes, function executions, and system events.
WebSocket Connection¶
Authentication¶
WebSocket connections require a valid JWT token. The token is passed as a query parameter in the connection URL. See the Service Account Authentication Guide for how to obtain a JWT token.
If the token is missing, invalid, or expired, the connection will be closed immediately with one of the following codes:
| Close Code | Meaning |
|---|---|
4001 | Unauthorized — missing or invalid token |
4002 | Missing workspace — workspaceSlug not provided |
4003 | Token expired — obtain a new token and reconnect |
Connection URL¶
Connect to the WebSocket server with your JWT token and workspace:
Native WebSocket (JavaScript)¶
// Create WebSocket connection
const token = 'YOUR_JWT_TOKEN'; // Obtain via Service Account or user login
const workspaceSlug = 'my-workspace';
const ws = new WebSocket(`wss://ws.centrali.io/?token=${encodeURIComponent(token)}&workspaceSlug=${workspaceSlug}`);
// Connection opened
ws.onopen = (event) => {
console.log('Connected to notifications');
};
// Listen for messages
ws.onmessage = (event) => {
const notification = JSON.parse(event.data);
console.log('Received notification:', notification);
// Notification structure:
// {
// id: 'notif_123',
// title: 'New Order',
// message: 'Order #12345 has been placed',
// type: 'info',
// read: false
// }
};
// Connection closed
ws.onclose = (event) => {
console.log('Disconnected from notifications', event.code, event.reason);
// Check close code for auth errors
if (event.code === 4001) console.error('Authentication failed');
if (event.code === 4003) console.error('Token expired — obtain a new token');
};
// Handle errors
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
Using with a WebSocket Library¶
// Using the 'ws' library in Node.js
import WebSocket from 'ws';
const token = 'YOUR_JWT_TOKEN';
const workspaceSlug = 'my-workspace';
const ws = new WebSocket(`wss://ws.centrali.io/?token=${encodeURIComponent(token)}&workspaceSlug=${workspaceSlug}`);
ws.on('open', () => {
console.log('Connected to Centrali notifications');
});
ws.on('message', (data) => {
const notification = JSON.parse(data.toString());
console.log('Notification:', notification);
handleNotification(notification);
});
ws.on('close', (code, reason) => {
console.log(`Connection closed: ${code} - ${reason}`);
// Implement reconnection logic if needed
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
function handleNotification(notification) {
switch(notification.type) {
case 'record_created':
console.log('New record created');
break;
case 'function_complete':
console.log('Function execution complete');
break;
case 'alert':
console.log('Alert:', notification.message);
break;
default:
console.log('Notification:', notification.message);
}
}
React Example¶
import { useEffect, useState } from 'react';
function useNotifications(token, workspaceSlug) {
const [notifications, setNotifications] = useState([]);
const [connected, setConnected] = useState(false);
useEffect(() => {
if (!token || !workspaceSlug) return;
const ws = new WebSocket(
`wss://ws.centrali.io/?token=${encodeURIComponent(token)}&workspaceSlug=${workspaceSlug}`
);
ws.onopen = () => {
setConnected(true);
console.log('Connected to notifications');
};
ws.onmessage = (event) => {
const notification = JSON.parse(event.data);
setNotifications(prev => [notification, ...prev]);
};
ws.onclose = (event) => {
setConnected(false);
if (event.code === 4003) {
console.log('Token expired — refresh and reconnect');
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
// Cleanup on unmount
return () => {
ws.close();
};
}, [token, workspaceSlug]);
return { notifications, connected };
}
// Usage in component
function NotificationPanel() {
const token = useAuthToken(); // Your auth token provider
const { notifications, connected } = useNotifications(token, 'my-workspace');
return (
<div>
<div>Status: {connected ? 'Connected' : 'Disconnected'}</div>
{notifications.map(notif => (
<div key={notif.id}>
<h3>{notif.title}</h3>
<p>{notif.message}</p>
</div>
))}
</div>
);
}
Notification Structure¶
Each notification message contains:
interface Notification {
id: string; // Unique notification ID
title: string; // Notification title
message: string; // Detailed message
type: string; // Type: 'info', 'success', 'warning', 'error', 'alert'
read: boolean; // Read status
timestamp?: string; // When the notification was created
metadata?: any; // Additional data specific to the notification type
}
Notification Types¶
System Notifications¶
- Workspace events
- User account updates
- System maintenance alerts
Data Notifications¶
- Record created
- Record updated
- Record deleted
- Bulk operation completed
Function Notifications¶
- Function execution started
- Function execution completed
- Function execution failed
- Function timeout
Custom Notifications¶
- Triggered from compute functions
- Application-specific events
Sending Notifications from Functions¶
While WebSocket connections are for receiving notifications, compute functions can trigger notifications that are delivered via WebSocket to connected users:
async function run() {
const orderId = executionParams.orderId;
// Process something
const order = await api.fetchRecord(orderId);
// Your processing logic here
api.log({ message: 'Order processed', orderId });
return { success: true, orderId };
}
Connection Management¶
Reconnection Strategy¶
When a connection drops, you should reconnect with exponential backoff. If the close code is 4003 (token expired), obtain a new token before reconnecting.
class NotificationManager {
constructor(getToken, workspaceSlug) {
this.getToken = getToken; // Function that returns a valid JWT token
this.workspaceSlug = workspaceSlug;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000; // Start with 1 second
}
async connect() {
const token = await this.getToken();
const url = `wss://ws.centrali.io/?token=${encodeURIComponent(token)}&workspaceSlug=${this.workspaceSlug}`;
this.ws = new WebSocket(url);
this.ws.onopen = () => {
console.log('Connected to notifications');
this.reconnectAttempts = 0;
this.reconnectDelay = 1000;
};
this.ws.onmessage = (event) => {
const notification = JSON.parse(event.data);
this.handleNotification(notification);
};
this.ws.onclose = (event) => {
console.log('Connection closed', event.code);
if (event.code === 4001) {
console.error('Authentication failed — check your token');
return; // Don't reconnect on auth failure
}
// For 4003 (expired) or normal closes, reconnect with a fresh token
this.attemptReconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnection attempts reached');
return;
}
this.reconnectAttempts++;
console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`);
setTimeout(() => {
this.connect();
}, this.reconnectDelay);
// Exponential backoff
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000); // Max 30 seconds
}
handleNotification(notification) {
// Your notification handling logic
console.log('Notification received:', notification);
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// Usage
const manager = new NotificationManager(
() => fetchAccessToken(), // Your token provider
'my-workspace'
);
manager.connect();
Best Practices¶
1. Connection Lifecycle¶
- Open connection when user logs in or enters workspace
- Close connection when user logs out or leaves
- Implement reconnection logic for network interruptions
2. Error Handling¶
- Always handle connection errors gracefully
- Provide user feedback when connection is lost
- Queue important actions during disconnection
3. Performance¶
- Avoid keeping connections open when not needed
- Use a single connection per user/workspace pair
- Process notifications efficiently to avoid blocking
4. Security¶
- Never expose sensitive data in notifications
- Always use
wss://(WebSocket Secure) — neverws://in production - Store JWT tokens securely and refresh before expiry
- Handle close code
4003by obtaining a fresh token before reconnecting
Limitations¶
- Maximum 1 connection per user per workspace
- Messages are not persisted - only real-time delivery
- Connection timeout after 5 minutes of inactivity
- Maximum message size: 64KB
Troubleshooting¶
Connection Fails¶
- Verify your JWT token is valid and not expired
- Verify
workspaceSlugis correct - Check if connection closed with code
4001(invalid token) or4002(missing workspace) - Ensure the user has access to the workspace
Not Receiving Notifications¶
- Confirm connection is established (check onopen)
- Verify notification events are being triggered
- Check browser console for errors
Connection Drops Frequently¶
- Implement reconnection logic
- Check for network stability
- Consider using heartbeat/ping messages
Related Documentation¶
- Compute Functions - Trigger notifications from functions
- Webhooks Guide - HTTP-based event notifications
- Blog Platform Example - Example implementation