Generic IVR Integration
Universal REST API integration pattern that works with any IVR platform.
Overview
This guide provides a platform-agnostic integration pattern for Sticky Calls API. Use this if you're working with:
- Custom IVR systems
- Legacy platforms
- Non-mainstream contact center software
- Home-grown solutions
- Any system that can make HTTP REST calls
Setup Time: 10-15 minutes Difficulty: Very Easy Prerequisites:
- Ability to make HTTP POST requests from your IVR
- Ability to parse JSON responses
- Sticky Calls API key
Integration Pattern
1. Call Start - Identify Caller
When: At the beginning of every call Purpose: Identify returning callers and retrieve context
HTTP Request:
POST /v1/calls/start HTTP/1.1
Host: api.stickycalls.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"call_id": "unique_call_identifier",
"identity_hints": {
"ani": "+14155551234",
"external_ids": {
"customer_id": "optional_crm_id"
}
}
}
HTTP Response (Returning Caller):
HTTP/1.1 200 OK
Content-Type: application/json
{
"call_id": "unique_call_identifier",
"customer_ref": "cust_a1b2c3d4",
"call_start": "2026-02-06T15:30:00.000Z",
"identity": {
"confidence": 0.95,
"level": "very_high",
"sources": ["ani:mobile", "external_id", "recency:1day"],
"recommendation": "reuse"
},
"open_intents": [
{
"intent": "follow_up_required",
"status": "open",
"attempt_count": 1
}
],
"variables": {
"last_issue": {
"value": "Customer called about billing issue. Refund processed.",
"source": null,
"ttl_seconds": 2592000
}
}
}
HTTP Response (New Caller):
HTTP/1.1 200 OK
Content-Type: application/json
{
"call_id": "unique_call_identifier",
"customer_ref": "cust_new123",
"call_start": "2026-02-06T15:30:00.000Z",
"identity": {
"confidence": 0.0,
"level": "low",
"sources": [],
"recommendation": "ignore"
},
"open_intents": [],
"variables": {}
}
2. Call End - Save Context
When: At the end of every call Purpose: Save conversation summary for future calls
HTTP Request:
POST /v1/calls/end HTTP/1.1
Host: api.stickycalls.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"call_id": "same_call_identifier_as_start",
"customer_ref": "cust_a1b2c3d4",
"intent": "billing_inquiry",
"intent_status": "resolved",
"variables": {
"resolution": "Issued $50 refund for duplicate charge",
"refund_amount": "50.00",
"issue_type": "billing"
}
}
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"call_id": "same_call_identifier_as_start",
"customer_ref": "cust_a1b2c3d4",
"call_end": "2026-02-06T15:45:00.000Z"
}
Implementation Examples
Curl (Testing)
Start Call:
curl -X POST https://api.stickycalls.com/v1/calls/start \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{
"call_id": "call_001",
"identity_hints": {
"ani": "+14155551234"
}
}'
End Call:
curl -X POST https://api.stickycalls.com/v1/calls/end \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{
"call_id": "call_001",
"customer_ref": "cust_abc123",
"intent": "general_inquiry",
"intent_status": "resolved",
"variables": {
"resolution": "Issue resolved"
}
}'
Python
import requests
API_KEY = "sk_test_abc123"
BASE_URL = "https://api.stickycalls.com"
def identify_caller(call_id, ani):
response = requests.post(
f"{BASE_URL}/v1/calls/start",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"call_id": call_id,
"identity_hints": {
"ani": ani
}
}
)
return response.json()
def save_context(call_id, customer_ref, intent, intent_status, variables):
response = requests.post(
f"{BASE_URL}/v1/calls/end",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"call_id": call_id,
"customer_ref": customer_ref,
"intent": intent,
"intent_status": intent_status,
"variables": variables
}
)
return response.json()
# Usage
result = identify_caller("call_001", "+14155551234")
if result["identity"]["confidence"] >= 0.7:
last_issue = result["variables"].get("last_issue", {}).get("value", "")
print(f"Welcome back! Last issue: {last_issue}")
else:
print("Welcome! First time caller.")
# At end of call
save_context(
call_id="call_001",
customer_ref=result["customer_ref"],
intent="general_inquiry",
intent_status="resolved",
variables={"resolution": "Issue resolved successfully"}
)
Node.js
const axios = require('axios');
const API_KEY = 'sk_test_abc123';
const BASE_URL = 'https://api.stickycalls.com';
async function identifyCaller(callId, ani) {
const response = await axios.post(
`${BASE_URL}/v1/calls/start`,
{
call_id: callId,
identity_hints: {
ani: ani
}
},
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
async function saveContext(callId, customerRef, intent, intentStatus, variables) {
const response = await axios.post(
`${BASE_URL}/v1/calls/end`,
{
call_id: callId,
customer_ref: customerRef,
intent: intent,
intent_status: intentStatus,
variables: variables
},
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Usage
(async () => {
const result = await identifyCaller('call_001', '+14155551234');
if (result.identity.confidence >= 0.7) {
const lastIssue = result.variables.last_issue?.value || '';
console.log(`Welcome back! Last issue: ${lastIssue}`);
} else {
console.log('Welcome! First time caller.');
}
// At end of call
await saveContext(
'call_001',
result.customer_ref,
'general_inquiry',
'resolved',
{ resolution: 'Issue resolved successfully' }
);
})();
PHP
<?php
$apiKey = 'sk_test_abc123';
$baseUrl = 'https://api.stickycalls.com';
function identifyCaller($callId, $ani) {
global $apiKey, $baseUrl;
$ch = curl_init("$baseUrl/v1/calls/start");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiKey",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'call_id' => $callId,
'identity_hints' => [
'ani' => $ani
]
]));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
function saveContext($callId, $customerRef, $intent, $intentStatus, $variables) {
global $apiKey, $baseUrl;
$ch = curl_init("$baseUrl/v1/calls/end");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiKey",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'call_id' => $callId,
'customer_ref' => $customerRef,
'intent' => $intent,
'intent_status' => $intentStatus,
'variables' => $variables
]));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Usage
$result = identifyCaller('call_001', '+14155551234');
if ($result['identity']['confidence'] >= 0.7) {
$lastIssue = $result['variables']['last_issue']['value'] ?? '';
echo "Welcome back! Last issue: " . $lastIssue;
} else {
echo "Welcome! First time caller.";
}
// At end of call
saveContext(
'call_001',
$result['customer_ref'],
'general_inquiry',
'resolved',
['resolution' => 'Issue resolved successfully']
);
?>
Decision Logic
Basic Personalization
IF identity.confidence >= 0.7:
Play personalized greeting
Show context to agent
Route to specialized queue
ELSE:
Play standard greeting
Standard routing
Confidence-Based Actions
IF identity.confidence >= 0.9:
High confidence - Full personalization
ELSE IF identity.confidence >= 0.7:
Medium confidence - Ask for verification
ELSE IF identity.confidence >= 0.3:
Low confidence - Standard flow with note
ELSE:
New caller - Standard onboarding
Open Intent Routing
FOR EACH intent IN open_intents:
IF intent.intent = "billing_issue" AND intent.status = "open":
Route to: Billing queue
Priority: High
ELSE IF intent.intent = "technical_support" AND intent.status = "open":
Route to: Technical queue
ELSE IF intent.intent = "escalation" AND intent.status = "open":
Route to: Senior agents
Priority: Urgent
IF no open intents found:
Route to: General queue
Error Handling
HTTP Status Codes
| Code | Meaning | Action |
|---|---|---|
| 200 | Success | Parse response, continue flow |
| 400 | Bad Request | Log error, use standard flow |
| 401 | Unauthorized | Check API key, alert admin |
| 402 | Payment Required | Alert admin (low credits) |
| 429 | Rate Limit | Retry after 1 second |
| 500 | Server Error | Log, use standard flow |
Timeout Handling
Set timeout: 5 seconds
ON TIMEOUT:
Log: "API timeout - continuing with standard flow"
Set: match_found = false
Continue: Standard greeting
Retry Logic
max_retries = 2
retry_delay = 1 second
FOR attempt IN 1 TO max_retries:
TRY:
response = call_api()
BREAK
CATCH timeout_error:
IF attempt < max_retries:
WAIT retry_delay
ELSE:
Use standard flow
Best Practices
1. Always Include call_id
Use a unique identifier for each call:
call_id = f"{platform_prefix}_{timestamp}_{uuid}"
Example: "pbx_20250128_abc123"
2. Normalize Phone Numbers
Format ANI consistently:
+14155551234 ✅ E.164 format (recommended)
14155551234 ✅ Also works
4155551234 ❌ Missing country code
3. Set Appropriate Timeouts
Connection timeout: 3 seconds
Read timeout: 5 seconds
Total timeout: 8 seconds max
4. Handle Failures Gracefully
Never block the call flow:
TRY:
result = identify_caller()
CATCH any_error:
result = {"match_found": false}
FINALLY:
Continue with call flow
5. Save Rich Context
Don't just save "resolved" - include details:
❌ variables: { "status": "resolved" }
✅ variables: {
"resolution": "Billing issue resolved. Refunded $50 for duplicate charge on 1/15.",
"refund_amount": "50.00",
"charge_date": "2026-01-15"
}
6. Use External IDs
Include CRM IDs when available:
{
"call_id": "call_001",
"identity_hints": {
"ani": "+14155551234",
"external_ids": {
"crm_customer_id": "CUST-12345",
"account_number": "ACC-67890"
}
}
}
Testing
1. Test API Connectivity
curl -v https://api.stickycalls.com/v1/health
# Expected: {"status":"healthy"}
2. Test Authentication
curl -X POST https://api.stickycalls.com/v1/calls/start \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{"call_id":"test","identity_hints":{"ani":"+14155551234"}}'
# Expected: 200 OK with JSON response
3. Test Match Logic
# First call (no match expected)
curl -X POST https://api.stickycalls.com/v1/calls/start \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{"call_id":"test_001","identity_hints":{"ani":"+14155559999"}}'
# Expected: identity.confidence = 0.0
# Save context (note: use customer_ref from response above)
curl -X POST https://api.stickycalls.com/v1/calls/end \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{
"call_id":"test_001",
"customer_ref":"cust_abc123",
"intent":"test_intent",
"intent_status":"resolved",
"variables":{"test_note":"Test context"}
}'
# Second call (match expected)
curl -X POST https://api.stickycalls.com/v1/calls/start \
-H "Authorization: Bearer sk_test_abc123" \
-H "Content-Type: application/json" \
-d '{"call_id":"test_002","identity_hints":{"ani":"+14155559999"}}'
# Expected: identity.confidence >= 0.5, variables.test_note.value = "Test context"
Monitoring
Key Metrics to Track
-
API Success Rate
- Target: > 99.5%
- Alert if < 95%
-
Average Response Time
- Target: < 200ms
- Alert if > 1000ms
-
Match Rate
- Track: Percentage of calls with identity.confidence >= 0.7
- Optimize: Aim for 20-40% (depends on call patterns)
-
Credit Usage
- Monitor: Daily/weekly credit consumption
- Alert: When < 20% remaining
Dashboard Integration
View all metrics in your Sticky Calls dashboard:
Troubleshooting
API Call Fails
Check:
- API key format:
Bearer sk_test_...orBearer sk_prod_... - URL correct:
https://api.stickycalls.com - Content-Type header:
application/json - Request body is valid JSON
No Match When Expected
Check:
- ANI format consistent between start/end calls
- call_id used correctly
- Context was actually saved (check logs)
- Not exceeding 90-day TTL
Credits Depleted
Actions:
- Purchase more credits at https://stickycalls.com/dashboard/billing
- Check for unexpected usage patterns
- Verify you're not charging for matches (should be free)
Next Steps
- View API Reference - Complete endpoint documentation
- Best Practices - Production optimization tips
- Five9 Integration - Platform-specific guide
- NICE CXone Integration - Platform-specific guide
- Contact Support - Get help
Ready to integrate? Sign up for free →