Pusher Real-Time Events
Real-time event notifications for the Circuit Auction backoffice using Pusher WebSocket technology.
About Pusher Events
The Circuit Auction API uses Pusher to provide real-time event notifications to connected clients. These events allow the backoffice application to react to changes as they happen, providing immediate feedback for operations like entity updates, queue processing, and bulk operations.
Setup & Installation
1. Include Pusher.js Library
Add the Pusher JavaScript library to your HTML:
<script src="https://js.pusher.com/8.0/pusher.min.js"></script>
Or install via npm:
npm install pusher-js
2. Get Pusher Configuration from API
Retrieve the Pusher configuration from the GET /api/me endpoint. This endpoint returns user information along with configuration settings including:
- pusherConfig.key: Your Pusher application key
- pusherConfig.cluster: The Pusher cluster region (e.g., 'eu', 'us2')
- pusherConfig.privateChannel: The private channel name to subscribe to
For authentication, you'll use the regular access token obtained from /api/login-token. See Authentication documentation for details.
Example API Request
// Get user configuration including Pusher settings
const ACCESS_TOKEN = localStorage.getItem('access_token'); // From /api/login-token
fetch(`https://backoffice.ddev.site/api/me?access_token=${ACCESS_TOKEN}`)
.then(response => response.json())
.then(data => {
const config = data.data[0].config;
const pusherKey = config.pusherConfig.key;
const pusherCluster = config.pusherConfig.cluster;
const privateChannel = config.pusherConfig.privateChannel;
console.log('Pusher Key:', pusherKey);
console.log('Pusher Cluster:', pusherCluster);
console.log('Private Channel:', privateChannel);
// Use these values to initialize Pusher (see below)
});
Example API Response (Relevant Fields)
{
"data": [{
"id": "1",
"label": "admin",
"config": {
...
"pusherConfig": {
"key": "e589860bfd6885f493bf",
"cluster": "eu",
"privateChannel": "private-general@backoffice-hk-eu-local"
},
...
}
}]
}
config.pusherConfig object within the API response. See the API /me Documentation for complete response details.
Pusher Configuration
Initialize Pusher Connection
Create a Pusher instance using configuration from /api/me:
// Get configuration from /api/me endpoint
const API_BASE_URL = 'https://backoffice.ddev.site';
const ACCESS_TOKEN = localStorage.getItem('access_token'); // From /api/login-token
fetch(`${API_BASE_URL}/api/me?access_token=${ACCESS_TOKEN}`)
.then(response => response.json())
.then(data => {
const config = data.data[0].config;
// Extract Pusher configuration from API response
const PUSHER_KEY = config.pusherConfig.key;
const PUSHER_CLUSTER = config.pusherConfig.cluster;
const PRIVATE_CHANNEL = config.pusherConfig.privateChannel;
// Initialize Pusher with regular access token for authentication
const pusher = new Pusher(PUSHER_KEY, {
cluster: PUSHER_CLUSTER,
encrypted: true,
forceTLS: true,
authEndpoint: `${API_BASE_URL}/api/v1.0/pusher_auth?access_token=${ACCESS_TOKEN}`
});
// Continue with channel subscription (see below)
setupPusherChannels(pusher, PRIVATE_CHANNEL);
});
/api/login-token for Pusher authentication. All Pusher settings (key, cluster, privateChannel) are available in config.pusherConfig.
Authentication
The Pusher connection requires authentication through the API's pusher_auth endpoint:
- Endpoint:
GET /api/v1.0/pusher_auth - Authentication: Pass your access token as a query parameter
- Channel Type: Private channel (requires authentication)
/api/login-token endpoint. See the Authentication documentation for details.
Subscribing to Events
Subscribe to Channel and Bind Events
// Subscribe to the private channel
const channel = pusher.subscribe(PRIVATE_CHANNEL);
// Bind to a specific event
channel.bind('entity_update', function(data) {
console.log('Entity updated:', data);
// Handle the event
if (data.bundle === 'item' && data.entity_id === currentItemId) {
console.log('Current item was updated');
// Refresh your UI or reload data
}
});
// Bind to multiple events
channel.bind('advanced_queue__post_execute', function(data) {
console.log('Queue task completed:', data.name);
if (data.name === 'print_statement_by_signal') {
console.log('Statement printing complete');
// Update PDF links or show notification
}
});
channel.bind('item_bulk', function(data) {
console.log('Bulk operation complete:', data);
alert(`Successfully processed ${data.count} items`);
});
Handle Connection Events
// Connection state changes
pusher.connection.bind('connected', function() {
console.log('Connected to Pusher');
});
pusher.connection.bind('disconnected', function() {
console.log('Disconnected from Pusher');
});
// Handle errors
pusher.connection.bind('error', function(error) {
console.error('Pusher connection error:', error);
if (error.error && error.error.data) {
console.error('Error code:', error.error.data.code);
console.error('Error message:', error.error.data.message);
}
});
Complete Example
// Full implementation example - get config from /api/me first
const API_BASE_URL = 'https://backoffice.ddev.site';
const ACCESS_TOKEN = localStorage.getItem('access_token'); // From /api/login-token
// Fetch configuration
fetch(`${API_BASE_URL}/api/me?access_token=${ACCESS_TOKEN}`)
.then(response => response.json())
.then(data => {
const config = data.data[0].config;
const PUSHER_KEY = config.pusherConfig.key;
const PUSHER_CLUSTER = config.pusherConfig.cluster;
const PRIVATE_CHANNEL = config.pusherConfig.privateChannel;
// Initialize Pusher with regular access token
const pusher = new Pusher(PUSHER_KEY, {
cluster: PUSHER_CLUSTER,
encrypted: true,
forceTLS: true,
authEndpoint: `${API_BASE_URL}/api/v1.0/pusher_auth?access_token=${ACCESS_TOKEN}`
});
// Subscribe to channel
const channel = pusher.subscribe(PRIVATE_CHANNEL);
// Listen for entity updates
channel.bind('entity_update', function(data) {
const userId = localStorage.getItem('user_id');
const shouldReload = data.force_reload || (data.uid === userId);
if (shouldReload && data.bundle === 'item') {
console.log(`Item ${data.entity_id} was updated`);
// Refresh the item data in your application
reloadItem(data.entity_id);
}
});
// Listen for queue completion
channel.bind('advanced_queue__post_execute', function(data) {
if (data.name === 'migrate_import_queue' && data.result) {
const successCount = data.result.result?.successes || 0;
console.log(`Import complete: ${successCount} items processed`);
// Show success message and refresh table
showNotification(`Import done! Processed ${successCount} items.`);
refreshDataTable();
}
});
// Handle connection errors
pusher.connection.bind('error', function(error) {
if (error.error?.data?.code === 4004) {
console.error('Over connection quota');
} else if (error.error?.data?.code === 4001) {
console.error('Application disabled');
}
});
})
.catch(error => {
console.error('Failed to initialize Pusher:', error);
});
Available Events
entity_update
Triggered when an entity (item, client, sale, task) is updated in the system.
Use Cases
- Reload item page when item data changes
- Refresh client information when client is updated
- Update sale details when sale is modified
- Refresh task list when task status changes
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Always "entity_update" |
bundle |
string | Entity type: "item", "client", "sale", "task" |
entity_id |
string | ID of the updated entity |
uid |
string | User ID who made the change |
force_reload |
boolean | Whether to force a page reload regardless of user |
Example Implementation
// Subscribe to entity_update event
channel.bind('entity_update', function(data) {
if (data.name === 'entity_update') {
const userId = localStorage.getItem('user_id');
const shouldReload = data.force_reload || (data.uid === userId);
if (shouldReload && data.bundle === 'item' && data.entity_id === currentItemId) {
// Reload the page to show updated item data
console.log('Reloading item:', data.entity_id);
fetchItemData(data.entity_id);
}
if (shouldReload && data.bundle === 'client' && data.entity_id === currentClientId) {
// Refresh client information
fetchClientData(data.entity_id);
}
if (shouldReload && data.bundle === 'sale' && data.entity_id === currentSaleId) {
// Refresh sale details
fetchSaleData(data.entity_id);
}
}
});
advanced_queue__post_execute
Triggered after a queue task completes execution. This is the most commonly used queue event.
Use Cases
- Update PDF links after statement generation
- Display sticker preview after print queue completion
- Refresh address export list after CSV generation
- Update order data after bulk operations
- Show import results after migration queue completes
- Update control list after verification tasks
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Queue task name (e.g., "print_statement_by_signal", "backoffice_print_sticker_queue") |
label |
string | Optional label for tracking (e.g., "via_bulk_{order_id}") |
result |
object | Task result data (structure varies by task type) |
result.nid |
string | Entity ID related to the task |
result.data |
object | Additional result data |
Common Task Names
print_statement_by_signal- Statement printing completionbackoffice_print_sticker_queue- Sticker printing completionExport addresses to CSV- Address export completionmigrate_import_queue- Migration import completioncontrol_list_ih_check- In-house control check completioncontrol_list_cs_check- Consignment statement check completioncontrol_list_order_check- Order control check completion
Example Implementation
// Subscribe to advanced_queue__post_execute event
channel.bind('advanced_queue__post_execute', function(data) {
// Handle statement printing completion
if (data.name === 'print_statement_by_signal' && data.result?.nid === entityId) {
// Update the last emailed invoice information
console.log('Statement printed for entity:', data.result.nid);
updateInvoiceDisplay(data.result);
}
// Handle sticker printing completion
if (data.name === 'backoffice_print_sticker_queue') {
console.log('Sticker print job completed');
if (data.result[0]?.print_job?.result?.result?.no_print_server) {
// Show sticker preview if no print server available
showStickerPreview(data.result[0].print_job.result.url);
}
}
// Handle bulk order operations
const bulkId = 'via_bulk_' + orderId;
if (data.label === bulkId) {
// Refresh order data after bulk operation
console.log('Bulk operation completed for order:', orderId);
refreshOrderData(data.result);
}
// Handle address export
if (data.name === 'Export addresses to CSV') {
console.log('Address export completed');
refreshAddressTable();
}
// Handle import completion
if (data.name === 'migrate_import_queue' && data.result) {
const successCount = data.result.result?.successes || 0;
showMessage('Import done! Total processed: ' + successCount);
}
// Handle control list checks
const controlTasks = ['control_list_ih_check', 'control_list_cs_check', 'control_list_order_check'];
if (controlTasks.includes(data.name)) {
console.log('Control check completed');
loadControlList();
}
});
advanced_queue__pre_execute
Triggered when a queue task begins execution, before processing starts.
Use Cases
- Display "processing" or "started" messages to users
- Initialize progress indicators
- Prepare UI for incoming results
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Queue task name (e.g., "migrate_import_queue", "knock_down_all_items") |
Example Implementation
// Subscribe to advanced_queue__pre_execute event
channel.bind('advanced_queue__pre_execute', function(data) {
if (data.name === 'migrate_import_queue') {
console.log('Import starting...');
showStatusMessage('Import Started...');
showProgressBar();
}
if (data.name === 'knock_down_all_items') {
console.log('Knock down process starting...');
showStatusMessage('Knocking down items...');
initializeProgressTracker();
}
});
advanced_queue__create
Triggered when a new queue task is created.
Use Cases
- Track queue task creation
- Monitor queue additions
- Display queue status updates
advanced_queue__update
Triggered when a queue task is updated (e.g., status change, progress update).
Use Cases
- Update queue task status
- Show progress updates
- Monitor queue changes
item_bulk
Triggered when bulk item operations complete (bulk updates, lot number assignments, revisions).
Use Cases
- Display bulk operation results
- Show success messages with item counts
- Update UI after mass item changes
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Operation name: "bulk_update", "item_lot_bulk", "item_bulk_retry", etc. |
type |
string | Operation type (e.g., "bulk") |
count |
integer | Number of items processed |
status |
string | Operation status: "success", "error", "warning" |
message |
string | Result message or error description |
Example Implementation
// Subscribe to item_bulk event
channel.bind('item_bulk', function(data) {
if (data.name === 'bulk_update' && data.type === 'bulk') {
console.log('Bulk update completed');
showSuccessMessage('Successfully created ' + data.count + ' revisions.');
refreshItemTable();
}
if (data.name === 'item_lot_bulk') {
console.log('Lot bulk operation completed');
showSuccessMessage('Successfully saved ' + data.count + ' items.');
refreshItemTable();
}
if (data.name === 'item_bulk_retry') {
console.log('Bulk retry completed with status:', data.status);
if (data.status === 'success') {
showSuccessMessage(data.message);
} else if (data.status === 'error') {
showErrorMessage(data.message);
} else if (data.status === 'warning') {
showWarningMessage(data.message);
}
}
});
caller_id
Triggered when an incoming phone call is detected, displaying caller information.
Use Cases
- Display caller ID popup
- Show client information for incoming calls
- Quick access to client records during calls
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Always "caller_id" |
username |
string | Username of the staff member receiving the call |
client |
object | Client information for the caller |
Example Implementation
// Subscribe to caller_id event
channel.bind('caller_id', function(data) {
if (data.name === 'caller_id') {
const currentUsername = localStorage.getItem('username');
if (currentUsername === data.username) {
// Display the caller ID popup with client information
console.log('Incoming call from client:', data.client);
showCallerIdPopup({
clientName: data.client.name,
clientId: data.client.id,
phoneNumber: data.client.phone
});
// Optional: Play notification sound
playNotificationSound();
// Optional: Create quick link to client page
const clientUrl = `/clients/${data.client.id}`;
createQuickAccessLink(clientUrl);
}
}
});
control_update
Triggered during control list verification tasks to provide progress updates.
Use Cases
- Display real-time progress during control checks
- Show number of items processed
- Update progress bars during verification
Event Data Structure
| Field | Type | Description |
|---|---|---|
name |
string | Task name: "control_list_ih_check", "control_list_cs_check", "control_list_order_check" |
total |
integer | Number of items processed so far |
Example Implementation
// Subscribe to control_update event
const controlListTasks = [
'control_list_ih_check',
'control_list_cs_check',
'control_list_order_check'
];
channel.bind('control_update', function(data) {
if (controlListTasks.includes(data.name)) {
console.log('Control check progress:', data.total, 'items processed');
// Update progress message
updateProgressMessage('Processed ' + data.total + ' items.');
// Update progress bar if you have one
updateProgressBar(data.total, data.expected_total);
}
});
knock_down_all_items
Triggered during bulk knock-down operations to provide progress updates.
Use Cases
- Display knock-down progress
- Show number of items knocked down
- Update progress bars during bulk knock-down
Event Data Structure
| Field | Type | Description |
|---|---|---|
result |
string | Progress message (e.g., "Knocked down 10 of 100 items...") |
Example Implementation
// Subscribe to knock_down_all_items event
channel.bind('knock_down_all_items', function(data) {
if (data.result) {
console.log('Knock down progress:', data.result);
// Update status message
updateStatusMessage(data.result);
// Extract progress from message (e.g., "Knocked down 10 of 100 items...")
const match = data.result.match(/(\d+)\s+of\s+(\d+)/);
if (match) {
const current = parseInt(match[1], 10);
const total = parseInt(match[2], 10);
const progressPercent = (current / total) * 100;
// Update progress bar
updateProgressBar(progressPercent);
// Update counter display
updateCounterDisplay(current, total);
console.log(`Progress: ${current}/${total} (${progressPercent.toFixed(1)}%)`);
}
}
});
Best Practices
1. Check Event Names
Always verify the data.name field to ensure you're handling the correct event, as multiple operations may use the same event type.
channel.bind('advanced_queue__post_execute', function(data) {
if (data.name === 'expected_task_name') {
// Handle specific task
}
});
2. User Filtering
For entity updates, check if the update came from the current user to avoid unnecessary reloads:
channel.bind('entity_update', function(data) {
const userId = localStorage.getItem('user_id');
const shouldReload = data.force_reload || (data.uid === userId);
if (shouldReload) {
// Reload or refresh data
}
});
3. Entity ID Matching
Verify that the event relates to the currently displayed entity:
channel.bind('entity_update', function(data) {
if (data.entity_id === currentEntityId && data.bundle === 'item') {
// Update current entity
fetchItemData(data.entity_id);
}
});
4. Safe Property Access
Use optional chaining or null checks when accessing nested properties:
channel.bind('advanced_queue__post_execute', function(data) {
// Using optional chaining (ES2020+)
if (data.result?.nid === currentId) {
handleResult(data.result);
}
// Or traditional null check
if (data.result && data.result.nid === currentId) {
handleResult(data.result);
}
});
5. Label Tracking for Bulk Operations
For bulk operations, use labels to match events with specific operations:
const bulkId = 'via_bulk_' + orderId;
channel.bind('advanced_queue__post_execute', function(data) {
if (data.label === bulkId) {
// Handle this specific bulk operation
console.log('Bulk operation completed for order:', orderId);
refreshOrderData(data.result);
}
});
6. Unbind Events When Done
Clean up event listeners when they're no longer needed to prevent memory leaks:
// Unbind a specific event
channel.unbind('entity_update');
// Unbind all events on a channel
channel.unbind_all();
// Unsubscribe from a channel
pusher.unsubscribe(PRIVATE_CHANNEL);
// Disconnect from Pusher entirely
pusher.disconnect();
7. Handle Reconnection
Implement reconnection logic to handle temporary connection issues:
pusher.connection.bind('disconnected', function() {
console.log('Disconnected from Pusher');
showOfflineIndicator();
});
pusher.connection.bind('connected', function() {
console.log('Reconnected to Pusher');
hideOfflineIndicator();
// Optionally refresh data after reconnection
refreshCurrentPageData();
});
8. Secure Your Access Token
Never expose your access token in client-side code or version control:
// GOOD: Get token from secure storage
const accessToken = localStorage.getItem('access_token');
// BAD: Hardcoding token in code
// const accessToken = 'abc123...';
Error Handling
Connection Errors
Handle Pusher connection errors to provide feedback to users:
pusher.connection.bind('error', function(error) {
console.error('Pusher connection error:', error);
// Extract error details
const errorMessage = error.error?.data?.message || 'Unknown error';
const errorCode = error.error?.data?.code || null;
// Log the error
console.error('Error code:', errorCode);
console.error('Error message:', errorMessage);
// Handle specific error codes
switch(errorCode) {
case 4001:
showError('Pusher application is disabled. Please contact support.');
break;
case 4003:
showError('Invalid Pusher configuration. Please check your settings.');
break;
case 4004:
showError('Connection limit reached. Please try again later.');
break;
case 4100:
showError('Too many connections. Please reduce concurrent connections.');
break;
case 4200:
showError('Connection error. Attempting to reconnect...');
break;
default:
showError('Connection error: ' + errorMessage);
}
// Optional: Send error to your error tracking service
logErrorToService({
type: 'pusher_error',
code: errorCode,
message: errorMessage
});
});
Common Error Codes
| Code | Description | Recommended Action |
|---|---|---|
4001 |
Application disabled or deleted | Contact administrator to check Pusher app status |
4003 |
Invalid Pusher key | Verify the Pusher key in your configuration |
4004 |
Over connection quota | Upgrade Pusher plan or reduce connections |
4100 |
Connection limit exceeded | Close unused connections or upgrade plan |
4200 |
Generic reconnect error | Wait and let Pusher auto-reconnect |
4201 |
Pong reply not received | Check network connectivity |
Connection State Monitoring
// Monitor all connection state changes
pusher.connection.bind('state_change', function(states) {
console.log('Connection state changed from', states.previous, 'to', states.current);
// Update UI based on connection state
switch(states.current) {
case 'connected':
showOnlineIndicator();
hideReconnectingMessage();
break;
case 'connecting':
showConnectingIndicator();
break;
case 'disconnected':
showOfflineIndicator();
break;
case 'unavailable':
showUnavailableMessage();
break;
case 'failed':
showFailedMessage();
break;
}
});
// Handle specific states
pusher.connection.bind('unavailable', function() {
console.warn('Pusher is unavailable');
showWarning('Real-time updates are temporarily unavailable');
});
pusher.connection.bind('failed', function() {
console.error('Pusher connection failed');
showError('Unable to establish real-time connection. Please refresh the page.');
});
Authentication Errors
Handle errors when subscribing to private channels:
const channel = pusher.subscribe(PRIVATE_CHANNEL);
channel.bind('pusher:subscription_error', function(status) {
console.error('Subscription error:', status);
if (status === 403) {
showError('Authentication failed. Please log in again.');
// Redirect to login page
window.location.href = '/login';
} else if (status === 408) {
showError('Subscription timeout. Please check your network connection.');
} else {
showError('Failed to subscribe to real-time updates.');
}
});
// Handle successful subscription
channel.bind('pusher:subscription_succeeded', function() {
console.log('Successfully subscribed to channel');
showSuccess('Real-time updates enabled');
});
Complete Working Example
Here's a complete example integrating all the concepts:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pusher Events Example</title>
<script src="https://js.pusher.com/8.0/pusher.min.js"></script>
</head>
<body>
<div id="status">Connecting...</div>
<div id="messages"></div>
<script>
// Configuration
const API_BASE_URL = 'https://backoffice.ddev.site';
const ACCESS_TOKEN = localStorage.getItem('access_token'); // From /api/login-token
const statusEl = document.getElementById('status');
const messagesEl = document.getElementById('messages');
// Fetch Pusher configuration from /api/me
fetch(`${API_BASE_URL}/api/me?access_token=${ACCESS_TOKEN}`)
.then(response => response.json())
.then(data => {
const config = data.data[0].config;
// Extract Pusher configuration
const PUSHER_KEY = config.pusherConfig.key;
const PUSHER_CLUSTER = config.pusherConfig.cluster;
const PRIVATE_CHANNEL = config.pusherConfig.privateChannel;
// Initialize Pusher with regular access token
const pusher = new Pusher(PUSHER_KEY, {
cluster: PUSHER_CLUSTER,
encrypted: true,
forceTLS: true,
authEndpoint: `${API_BASE_URL}/api/v1.0/pusher_auth?access_token=${ACCESS_TOKEN}`
});
// Monitor connection state
pusher.connection.bind('state_change', function(states) {
statusEl.textContent = `Status: ${states.current}`;
console.log('State changed:', states.previous, '->', states.current);
});
pusher.connection.bind('connected', function() {
statusEl.textContent = 'Connected';
statusEl.style.color = 'green';
});
pusher.connection.bind('error', function(error) {
const errorCode = error.error?.data?.code;
const errorMsg = error.error?.data?.message || 'Unknown error';
statusEl.textContent = `Error: ${errorMsg}`;
statusEl.style.color = 'red';
console.error('Connection error:', errorCode, errorMsg);
});
// Subscribe to private channel
const channel = pusher.subscribe(PRIVATE_CHANNEL);
// Handle subscription events
channel.bind('pusher:subscription_succeeded', function() {
console.log('Successfully subscribed to channel');
addMessage('Subscribed to real-time events', 'success');
});
channel.bind('pusher:subscription_error', function(status) {
console.error('Subscription failed:', status);
addMessage('Failed to subscribe: ' + status, 'error');
});
// Subscribe to application events
channel.bind('entity_update', function(data) {
console.log('Entity updated:', data);
addMessage(`${data.bundle} updated: ${data.entity_id}`, 'info');
});
channel.bind('advanced_queue__post_execute', function(data) {
console.log('Queue task completed:', data.name);
addMessage(`Task completed: ${data.name}`, 'success');
});
channel.bind('item_bulk', function(data) {
console.log('Bulk operation:', data);
addMessage(`Bulk ${data.name}: ${data.count} items`, 'info');
});
channel.bind('caller_id', function(data) {
console.log('Incoming call:', data);
addMessage(`Incoming call from: ${data.client?.name || 'Unknown'}`, 'warning');
});
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
pusher.disconnect();
});
})
.catch(error => {
console.error('Failed to initialize Pusher:', error);
statusEl.textContent = 'Failed to load configuration';
statusEl.style.color = 'red';
});
// Helper function to display messages
function addMessage(text, type = 'info') {
const msg = document.createElement('div');
msg.textContent = `[${new Date().toLocaleTimeString()}] ${text}`;
msg.style.padding = '5px';
msg.style.margin = '5px 0';
msg.style.borderLeft = '3px solid ' + getColor(type);
messagesEl.insertBefore(msg, messagesEl.firstChild);
}
function getColor(type) {
const colors = {
success: '#28a745',
error: '#dc3545',
warning: '#ffc107',
info: '#17a2b8'
};
return colors[type] || colors.info;
}
</script>
</body>
</html>
Testing Pusher Connection
Debug Mode
Enable Pusher's debug mode to see detailed logging:
// Enable Pusher logging (for development only)
Pusher.logToConsole = true;
const pusher = new Pusher(PUSHER_KEY, {
cluster: PUSHER_CLUSTER,
encrypted: true,
forceTLS: true,
authEndpoint: authEndpoint
});
Test Connection
Verify your Pusher connection is working:
// Check connection state
console.log('Connection state:', pusher.connection.state);
// Available states: initialized, connecting, connected, unavailable, failed, disconnected
// Manually trigger connection (if needed)
pusher.connect();
// Check if connected
if (pusher.connection.state === 'connected') {
console.log('Pusher is connected');
} else {
console.log('Pusher is not connected:', pusher.connection.state);
}
Verify Channel Subscription
// Get channel object
const channel = pusher.channel(PRIVATE_CHANNEL);
if (channel && channel.subscribed) {
console.log('Successfully subscribed to channel');
} else {
console.log('Not subscribed to channel');
}
// List all subscribed channels
console.log('All channels:', pusher.allChannels());
Test Event Reception
Use Pusher's debug console to send test events:
- Go to your Pusher dashboard at dashboard.pusher.com
- Select your application
- Navigate to "Debug Console"
- Send a test event to your private channel to verify it's received
/api/v1.0/pusher_auth endpoint.
Quick Reference
| Event Name | Purpose | Frequency |
|---|---|---|
entity_update |
Entity changes (items, clients, sales, tasks) | Very Common |
advanced_queue__post_execute |
Queue task completion | Very Common |
advanced_queue__pre_execute |
Queue task start | Common |
item_bulk |
Bulk item operations | Common |
control_update |
Control verification progress | Occasional |
knock_down_all_items |
Bulk knock-down progress | Occasional |
caller_id |
Incoming call notifications | Occasional |
advanced_queue__create |
Queue task creation | Rare (available but unused) |
advanced_queue__update |
Queue task updates | Rare (available but unused) |