How to set up a Genesys Cloud webhook that sends Slack notifications on queue SLA breach
What You Will Build
- You will create a Genesys Cloud Webhook that triggers when a conversation in a specific queue exceeds its Service Level Agreement (SLA) target.
- This tutorial uses the Genesys Cloud Platform APIs and the Python
PureCloudPlatformClientV2SDK. - The implementation covers Python for the webhook configuration and a Node.js serverless function (AWS Lambda style) to receive the payload and post to Slack.
Prerequisites
- Genesys Cloud Account: An admin user with
webhooks:writepermissions. - OAuth Credentials: A Genesys Cloud OAuth client with
webhooks:writeandrouting:queue:readscopes. - Slack Bot Token: A
xoxb-token withchat:writescope for the target channel. - Python Environment: Python 3.8+ with
pip. - Node.js Environment: Node.js 14+ for the webhook receiver service.
- External Dependencies:
- Python:
purecloudplatformclientv2,requests - Node.js:
axios,dotenv
- Python:
Authentication Setup
Genesys Cloud uses OAuth 2.0 for API access. You must obtain an access token using the Client Credentials Grant flow. This token expires after 30 minutes, so production code must implement refresh logic.
Python OAuth Helper
import os
from purecloudplatformclientv2 import ApiClient, Configuration, WebhooksApi, Webhook
from purecloudplatformclientv2.rest import ApiException
import requests
# Configuration
CLIENT_ID = os.getenv("GENESYS_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET")
ENVIRONMENT = os.getenv("GENESYS_ENVIRONMENT", "mypurecloud.com")
def get_access_token() -> str:
"""
Retrieves an OAuth2 access token from Genesys Cloud.
In production, cache this token and refresh before expiration.
"""
url = f"https://api.{ENVIRONMENT}/oauth/token"
data = {
'grant_type': 'client_credentials'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
# Basic Auth header for client credentials
import base64
credentials = f"{CLIENT_ID}:{CLIENT_SECRET}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()
headers['Authorization'] = f"Basic {encoded_credentials}"
response = requests.post(url, data=data, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to get token: {response.text}")
return response.json().get('access_token')
def get_genesys_api_client():
"""
Configures and returns the Genesys Cloud API Client.
"""
configuration = Configuration(
host=f"https://api.{ENVIRONMENT}",
access_token=get_access_token()
)
return ApiClient(configuration)
Implementation
Step 1: Identify the Trigger and Queue
Before creating the webhook, you must identify the Queue ID you wish to monitor. Genesys Cloud webhooks for routing events require the Queue ID to filter triggers effectively.
- Get Queue ID: Use the
RoutingApito list queues. - Define SLA Breach Logic: The webhook trigger event for SLA breaches is
routing:conversation:queue:sla:breach. This event fires when a conversation has been in the queue longer than the defined service level target (e.g., 80% of conversations answered within 20 seconds).
Note: The routing:conversation:queue:sla:breach event is a standard platform event. It does not require custom scripting on the Genesys side.
Step 2: Create the Webhook via Python SDK
You will create a webhook that points to your external service (e.g., AWS Lambda, Azure Function, or a custom Node.js server).
def create_sla_breach_webhook(queue_id: str, target_url: str):
"""
Creates a Genesys Cloud webhook for SLA breaches.
Args:
queue_id: The ID of the queue to monitor.
target_url: The HTTPS URL of your receiver service.
"""
api_client = get_genesys_api_client()
webhooks_api = WebhooksApi(api_client)
# Define the webhook configuration
# The 'events' array specifies which triggers activate this webhook.
# We use 'routing:conversation:queue:sla:breach'.
webhook_body = Webhook(
name="Queue SLA Breach Alert",
description="Sends Slack notification when queue SLA is breached",
target_uri=target_url,
events=["routing:conversation:queue:sla:breach"],
# Optional: Filter by specific queue to avoid noise
# Note: Filtering by queue ID is done in the event payload processing
# or via Webhook Filters if supported by your specific license/region.
# For this example, we assume the receiver filters by queue ID.
enabled=True,
# Security: Use a secret to verify the request signature
security_token=os.getenv("WEBHOOK_SECRET_TOKEN")
)
try:
response = webhooks_api.post_webhook(body=webhook_body)
print(f"Webhook created successfully with ID: {response.id}")
return response.id
except ApiException as e:
print(f"Exception when calling WebhooksApi->post_webhook: {e}")
raise
Step 3: Build the Receiver Service (Node.js)
Genesys Cloud sends a POST request to your target_uri. You must implement a server that:
- Validates the request signature (using
security_token). - Parses the payload.
- Posts a formatted message to Slack.
Project Setup
mkdir genesys-slack-bot
cd genesys-slack-bot
npm init -y
npm install axios dotenv
Receiver Code (index.js)
require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');
const app = express();
app.use(express.json());
// Configuration
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
const SLACK_CHANNEL_ID = process.env.SLACK_CHANNEL_ID;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET_TOKEN;
// Verify Genesys Cloud Webhook Signature
function verifySignature(req, res, buf) {
if (!WEBHOOK_SECRET) return true; // Skip if no secret configured
const signature = req.headers['x-genesys-signature'];
const timestamp = req.headers['x-genesys-timestamp'];
if (!signature || !timestamp) {
return false;
}
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
hmac.update(timestamp + buf.toString());
const digest = hmac.digest('hex');
// Simple comparison; in production, use a timing-safe comparison
return signature === `sha256=${digest}`;
}
// Post to Slack
async function postToSlack(message) {
try {
const response = await axios.post('https://slack.com/api/chat.postMessage', {
channel: SLACK_CHANNEL_ID,
text: message
}, {
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json'
}
});
if (!response.data.ok) {
console.error('Slack API Error:', response.data.error);
}
} catch (error) {
console.error('Error posting to Slack:', error.message);
}
}
// Webhook Endpoint
app.post('/webhook/genesys', (req, res) => {
// Verify signature
if (!verifySignature(req, res, JSON.stringify(req.body))) {
return res.status(401).send('Unauthorized');
}
const payload = req.body;
const event = payload.event;
// Check if this is an SLA breach event
if (event === 'routing:conversation:queue:sla:breach') {
const queueName = payload.data.queue.name;
const waitTime = payload.data.waitTimeSeconds;
const slaTarget = payload.data.slaTargetSeconds;
const conversationId = payload.data.conversationId;
// Format Slack Message
const slackMessage = `
*🚨 SLA Breach Alert*
*Queue:* ${queueName}
*Wait Time:* ${waitTime}s (Target: ${slaTarget}s)
*Conversation ID:* ${conversationId}
`.trim();
// Post to Slack
postToSlack(slackMessage);
// Acknowledge receipt to Genesys Cloud
res.status(200).send('OK');
} else {
// Ignore other events
res.status(200).send('Ignored');
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook receiver listening on port ${PORT}`);
});
Step 4: Deploy and Connect
- Deploy the Node.js Service: Use a platform like AWS Lambda, Heroku, or a simple EC2 instance. Ensure the endpoint is publicly accessible via HTTPS (Genesys Cloud requires HTTPS for webhooks).
- Note the Public URL: For example,
https://my-service.herokuapp.com/webhook/genesys. - Run the Python Script: Execute the Python script to create the webhook, passing the Queue ID and the Public URL.
if __name__ == "__main__":
QUEUE_ID = "your-queue-id-here"
TARGET_URL = "https://my-service.herokuapp.com/webhook/genesys"
create_sla_breach_webhook(QUEUE_ID, TARGET_URL)
Complete Working Example
Python (create_webhook.py)
import os
import sys
from purecloudplatformclientv2 import ApiClient, Configuration, WebhooksApi, Webhook
from purecloudplatformclientv2.rest import ApiException
import requests
import base64
def get_access_token() -> str:
CLIENT_ID = os.getenv("GENESYS_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET")
ENVIRONMENT = os.getenv("GENESYS_ENVIRONMENT", "mypurecloud.com")
url = f"https://api.{ENVIRONMENT}/oauth/token"
data = {'grant_type': 'client_credentials'}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
credentials = f"{CLIENT_ID}:{CLIENT_SECRET}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()
headers['Authorization'] = f"Basic {encoded_credentials}"
response = requests.post(url, data=data, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to get token: {response.text}")
return response.json().get('access_token')
def create_sla_breach_webhook(queue_id: str, target_url: str):
configuration = Configuration(
host=f"https://api.{os.getenv('GENESYS_ENVIRONMENT', 'mypurecloud.com')}",
access_token=get_access_token()
)
api_client = ApiClient(configuration)
webhooks_api = WebhooksApi(api_client)
webhook_body = Webhook(
name="Queue SLA Breach Alert",
description="Sends Slack notification when queue SLA is breached",
target_uri=target_url,
events=["routing:conversation:queue:sla:breach"],
enabled=True,
security_token=os.getenv("WEBHOOK_SECRET_TOKEN")
)
try:
response = webhooks_api.post_webhook(body=webhook_body)
print(f"Webhook created successfully with ID: {response.id}")
return response.id
except ApiException as e:
print(f"Exception when calling WebhooksApi->post_webhook: {e}")
raise
if __name__ == "__main__":
if not os.getenv("GENESYS_CLIENT_ID") or not os.getenv("GENESYS_CLIENT_SECRET"):
print("Error: GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET environment variables are required.")
sys.exit(1)
QUEUE_ID = os.getenv("QUEUE_ID", "default-queue-id")
TARGET_URL = os.getenv("TARGET_URL", "https://example.com/webhook")
create_sla_breach_webhook(QUEUE_ID, TARGET_URL)
Node.js (index.js)
require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');
const app = express();
app.use(express.json());
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
const SLACK_CHANNEL_ID = process.env.SLACK_CHANNEL_ID;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET_TOKEN;
function verifySignature(req, buf) {
if (!WEBHOOK_SECRET) return true;
const signature = req.headers['x-genesys-signature'];
const timestamp = req.headers['x-genesys-timestamp'];
if (!signature || !timestamp) return false;
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
hmac.update(timestamp + buf.toString());
const digest = hmac.digest('hex');
return signature === `sha256=${digest}`;
}
async function postToSlack(message) {
try {
const response = await axios.post('https://slack.com/api/chat.postMessage', {
channel: SLACK_CHANNEL_ID,
text: message
}, {
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json'
}
});
if (!response.data.ok) console.error('Slack Error:', response.data.error);
} catch (error) {
console.error('Slack Post Error:', error.message);
}
}
app.post('/webhook/genesys', (req, res) => {
if (!verifySignature(req, JSON.stringify(req.body))) {
return res.status(401).send('Unauthorized');
}
const payload = req.body;
if (payload.event === 'routing:conversation:queue:sla:breach') {
const data = payload.data;
const slackMessage = `
*🚨 SLA Breach Alert*
*Queue:* ${data.queue.name}
*Wait:* ${data.waitTimeSeconds}s / *Target:* ${data.slaTargetSeconds}s
*ID:* ${data.conversationId}
`.trim();
postToSlack(slackMessage);
}
res.status(200).send('OK');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on ${PORT}`));
Common Errors & Debugging
Error: 401 Unauthorized
- Cause: The OAuth token is invalid, expired, or the client credentials are incorrect.
- Fix: Verify
GENESYS_CLIENT_IDandGENESYS_CLIENT_SECRET. Ensure the client haswebhooks:writescope. Check that the token is being refreshed before it expires.
Error: 403 Forbidden
- Cause: The OAuth client does not have the necessary permissions.
- Fix: Ensure the OAuth client is associated with an Admin user who has
webhooks:writeandrouting:queue:readpermissions in the Genesys Cloud Admin Console.
Error: 405 Method Not Allowed
- Cause: The target URL does not accept POST requests.
- Fix: Ensure your Node.js endpoint is configured to handle POST requests (
app.post('/webhook/genesys', ...)).
Error: Webhook Not Firing
- Cause: The queue does not have an SLA target configured, or no conversations are breaching the SLA.
- Fix: Verify the queue has an SLA target set (e.g., 80% in 20 seconds). Generate test traffic to the queue that exceeds the wait time. Check the Webhooks section in the Genesys Cloud Admin Console to view delivery logs.
Error: Slack Post Failure
- Cause: Invalid Slack Bot Token or Channel ID.
- Fix: Ensure the Slack Bot Token (
xoxb-) haschat:writescope. Ensure the Channel ID is correct (starts withC). Verify the bot is added to the target channel.