Triggering a NICE CXone Personal Connection Outbound Call
What You Will Build
- This tutorial demonstrates how to programmatically initiate an outbound call from a specific agent’s device using the NICE CXone Personal Connection API.
- This solution utilizes the NICE CXone REST API endpoint
/api/journey/v1/outboundto submit a call request. - The implementation is provided in Python using the
requestslibrary for maximum transparency into HTTP headers and payloads.
Prerequisites
- OAuth Client Type: You require an OAuth Client configured with the
cxonescope. Typically, this is a “Server-to-Server” or “Confidential Client” setup if running on a backend, or a “Public Client” if running in a browser-based extension (though CORS restrictions apply). - Required Scopes: The token must include the
outbound:callscope. Without this, the API will return a 403 Forbidden error. - SDK/API Version: This tutorial uses the raw REST API v1 for the Journey/Outbound module. No specific SDK is required, but you must understand JSON serialization.
- Language/Runtime Requirements: Python 3.8+ is required.
- External Dependencies:
pip install requests python-dotenv - Environment Variables: You must have the following values available:
CXONE_OAUTH_CLIENT_IDCXONE_OAUTH_CLIENT_SECRETCXONE_OAUTH_GRANT_TYPE(usuallyclient_credentials)CXONE_BASE_URL(e.g.,https://api-us-2.cxone.com)AGENT_ID(The unique ID of the agent who will receive the call)TARGET_NUMBER(The E.164 formatted number to call)
Authentication Setup
NICE CXone uses OAuth 2.0 for authentication. You must obtain an access token before making any API calls. The token expires after a set duration (usually 3600 seconds), so production code should handle token caching and refresh. For this tutorial, we will fetch a fresh token on execution.
The endpoint for token retrieval is {BASE_URL}/oauth/token.
import requests
import os
import json
from typing import Optional
def get_access_token() -> str:
"""
Retrieves an OAuth 2.0 access token from NICE CXone.
Raises an exception if authentication fails.
"""
base_url = os.getenv("CXONE_BASE_URL")
client_id = os.getenv("CXONE_OAUTH_CLIENT_ID")
client_secret = os.getenv("CXONE_OAUTH_CLIENT_SECRET")
grant_type = os.getenv("CXONE_OAUTH_GRANT_TYPE", "client_credentials")
if not all([base_url, client_id, client_secret]):
raise ValueError("Missing required environment variables for OAuth.")
token_url = f"{base_url}/oauth/token"
# The body for client_credentials grant
payload = {
"grant_type": grant_type,
"client_id": client_id,
"client_secret": client_secret
}
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
try:
response = requests.post(token_url, data=payload, headers=headers)
response.raise_for_status()
token_data = response.json()
access_token = token_data.get("access_token")
if not access_token:
raise KeyError("Access token not found in response.")
return access_token
except requests.exceptions.HTTPError as http_err:
# Handle specific OAuth errors
if response.status_code == 401:
print(f"Authentication failed: Invalid credentials or client configuration.")
elif response.status_code == 403:
print(f"Authentication failed: Client does not have permission to request tokens.")
else:
print(f"HTTP error occurred: {http_err}")
raise
except requests.exceptions.RequestException as req_err:
print(f"Network error occurred: {req_err}")
raise
Implementation
Step 1: Constructing the Outbound Call Payload
The core of the Personal Connection API lies in the payload structure. Unlike standard predictive dialing, Personal Connection is an “on-demand” call triggered by an external event (like a CRM click-to-call).
The API endpoint is POST /api/journey/v1/outbound.
Critical Payload Parameters:
outboundCampaignId: You must target a specific Outbound Campaign within CXone. This campaign must be configured to allow “Personal Connection” or “API-initiated” calls.contactId: The ID of the contact record in CXone. If you do not have a contact record, you must create one first via the Contact API.phoneNumber: The E.164 formatted number to dial.agentId: The ID of the agent who should receive the inbound leg of the call.
If you do not provide an agentId, the system may attempt to assign one based on campaign routing rules, but for Personal Connection, explicitly defining the agent is the standard pattern.
def build_outbound_payload(agent_id: str, contact_id: str, target_number: str, campaign_id: str) -> dict:
"""
Constructs the JSON payload for the Personal Connection API call.
"""
payload = {
"outboundCampaignId": campaign_id,
"contactId": contact_id,
"phoneNumber": target_number,
"agentId": agent_id,
# Optional: Pass custom data to the journey context
"data": {
"source": "external_api",
"context_note": "Triggered via Developer Tutorial"
}
}
return payload
Step 2: Executing the API Call
Now we combine the authentication and payload construction to make the actual call. We use the requests library to send the POST request.
Important Note on Headers:
Authorization: Bearer {access_token}Content-Type: application/jsonAccept: application/json
def trigger_personal_connection_call(
access_token: str,
agent_id: str,
contact_id: str,
target_number: str,
campaign_id: str
) -> dict:
"""
Triggers an outbound call using the Personal Connection API.
Returns the API response JSON.
"""
base_url = os.getenv("CXONE_BASE_URL")
endpoint = f"{base_url}/api/journey/v1/outbound"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "application/json"
}
payload = build_outbound_payload(agent_id, contact_id, target_number, campaign_id)
try:
response = requests.post(endpoint, json=payload, headers=headers)
# Log the status code for debugging
print(f"API Response Status: {response.status_code}")
# Raise an exception for 4xx and 5xx status codes
response.raise_for_status()
result = response.json()
print("Call triggered successfully.")
print(json.dumps(result, indent=2))
return result
except requests.exceptions.HTTPError as http_err:
# Parse the error response from CXone
try:
error_details = response.json()
print(f"API Error: {error_details}")
except ValueError:
print(f"API Error: Could not parse error response. Status: {http_err}")
raise
except requests.exceptions.ConnectionError:
print("Failed to connect to CXone API. Check your network or base URL.")
raise
except requests.exceptions.Timeout:
print("Request to CXone API timed out.")
raise
Step 3: Handling Edge Cases and Validation
Before calling the API, you must validate that the agentId is currently online and available to receive calls. If the agent is offline, in a break, or already on a call, the Personal Connection request may fail or queue unexpectedly depending on campaign settings.
While the API returns an error if the agent is invalid, it is best practice to check agent status beforehand using the /api/v2/users/{userId}/interaction or presence API. For brevity, we assume the agent ID is valid in the main example, but you should implement a wrapper that checks:
- Is the user active?
- Is the user’s current presence “Available” or “Ready”?
- Is the user currently engaged in another interaction?
If these checks fail, you should abort the API call and notify the user in your UI.
Complete Working Example
Below is the full, copy-pasteable Python script. Ensure you have a .env file in the same directory with your credentials.
#!/usr/bin/env python3
"""
NICE CXone Personal Connection Outbound Call Trigger
====================================================
This script demonstrates how to trigger an outbound call from a specific agent
using the NICE CXone Personal Connection API.
Requirements:
- pip install requests python-dotenv
- .env file with CXONE_OAUTH_CLIENT_ID, CXONE_OAUTH_CLIENT_SECRET, CXONE_BASE_URL
"""
import os
import sys
import json
import requests
from dotenv import load_dotenv
from typing import Dict, Any
# Load environment variables from .env file
load_dotenv()
def get_access_token() -> str:
"""
Retrieves an OAuth 2.0 access token from NICE CXone.
"""
base_url = os.getenv("CXONE_BASE_URL")
client_id = os.getenv("CXONE_OAUTH_CLIENT_ID")
client_secret = os.getenv("CXONE_OAUTH_CLIENT_SECRET")
grant_type = os.getenv("CXONE_OAUTH_GRANT_TYPE", "client_credentials")
if not all([base_url, client_id, client_secret]):
raise ValueError("Missing required environment variables: CXONE_BASE_URL, CXONE_OAUTH_CLIENT_ID, CXONE_OAUTH_CLIENT_SECRET")
token_url = f"{base_url}/oauth/token"
payload = {
"grant_type": grant_type,
"client_id": client_id,
"client_secret": client_secret
}
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
try:
response = requests.post(token_url, data=payload, headers=headers)
response.raise_for_status()
token_data = response.json()
access_token = token_data.get("access_token")
if not access_token:
raise KeyError("Access token not found in response.")
return access_token
except requests.exceptions.HTTPError as http_err:
print(f"OAuth Authentication failed with status {response.status_code}: {response.text}")
sys.exit(1)
except requests.exceptions.RequestException as req_err:
print(f"Network error during OAuth: {req_err}")
sys.exit(1)
def trigger_personal_connection_call(
access_token: str,
agent_id: str,
contact_id: str,
target_number: str,
campaign_id: str
) -> Dict[str, Any]:
"""
Triggers an outbound call using the Personal Connection API.
"""
base_url = os.getenv("CXONE_BASE_URL")
endpoint = f"{base_url}/api/journey/v1/outbound"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "application/json"
}
# Construct the payload
payload = {
"outboundCampaignId": campaign_id,
"contactId": contact_id,
"phoneNumber": target_number,
"agentId": agent_id,
"data": {
"source": "developer_tutorial",
"note": "Test call from Python script"
}
}
print(f"Triggering call for Agent: {agent_id} to Number: {target_number}")
print(f"Using Campaign: {campaign_id}, Contact: {contact_id}")
try:
response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
print(f"API Response Status: {response.status_code}")
# Raise an exception for 4xx and 5xx status codes
response.raise_for_status()
result = response.json()
print("Success: Call triggered.")
print(json.dumps(result, indent=2))
return result
except requests.exceptions.HTTPError as http_err:
print(f"API Error ({response.status_code}):")
try:
error_details = response.json()
print(json.dumps(error_details, indent=2))
except ValueError:
print(response.text)
return None
except requests.exceptions.ConnectionError:
print("Failed to connect to CXone API. Check your network or base URL.")
return None
except requests.exceptions.Timeout:
print("Request to CXone API timed out.")
return None
def main():
"""
Main execution block.
Replace the placeholder values below with actual IDs from your CXone instance.
"""
# --- CONFIGURATION ---
# In a production app, these would come from a database or UI input
AGENT_ID = os.getenv("AGENT_ID", "00000000-0000-0000-0000-000000000000")
CONTACT_ID = os.getenv("CONTACT_ID", "00000000-0000-0000-0000-000000000000")
TARGET_NUMBER = os.getenv("TARGET_NUMBER", "+15551234567")
CAMPAIGN_ID = os.getenv("CAMPAIGN_ID", "00000000-0000-0000-0000-000000000000")
# ---------------------
if all([AGENT_ID == "00000000-0000-0000-0000-000000000000",
CONTACT_ID == "00000000-0000-0000-0000-000000000000",
CAMPAIGN_ID == "00000000-0000-0000-0000-000000000000"]):
print("Error: Please set AGENT_ID, CONTACT_ID, and CAMPAIGN_ID in your .env file.")
sys.exit(1)
try:
# Step 1: Get Token
print("Fetching OAuth Token...")
access_token = get_access_token()
print("Token acquired.")
# Step 2: Trigger Call
result = trigger_personal_connection_call(
access_token=access_token,
agent_id=AGENT_ID,
contact_id=CONTACT_ID,
target_number=TARGET_NUMBER,
campaign_id=CAMPAIGN_ID
)
if result:
print("Process completed.")
else:
print("Process failed.")
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 401 Unauthorized
- Cause: The OAuth token is invalid, expired, or missing.
- Fix: Verify that your
CXONE_OAUTH_CLIENT_IDandCXONE_OAUTH_CLIENT_SECRETare correct. Ensure the client is enabled in the CXone Admin portal. Check that the token was successfully retrieved in Step 1.
Error: 403 Forbidden
- Cause: The OAuth client does not have the required scope (
outbound:call), or the user associated with the token does not have permission to use Personal Connection. - Fix: Go to the CXone Admin portal > Security > OAuth Clients. Edit your client and ensure
outbound:callis checked. Also, verify that the agent ID provided is assigned to the campaign specified inoutboundCampaignId.
Error: 400 Bad Request
- Cause: Invalid payload structure. Common issues include:
contactIddoes not exist.phoneNumberis not in E.164 format (e.g., missing+prefix).campaignIddoes not exist or is not configured for Personal Connection.agentIdis not assigned to the campaign.
- Fix: Validate the UUIDs against your CXone instance. Use the CXone Contact API to verify the
contactIdexists. Ensure the phone number starts with+. Check the Outbound Campaign settings to ensure “API Initiated” or “Personal Connection” is enabled.
Error: 429 Too Many Requests
- Cause: You have exceeded the rate limit for the Outbound API.
- Fix: Implement exponential backoff retry logic. The response headers
Retry-Aftermay indicate how long to wait. Do not retry immediately.
Error: 500 Internal Server Error
- Cause: A transient error on the CXone platform.
- Fix: Retry the request after a short delay. If it persists, contact NICE CXone support with the request ID found in the response headers (if available).