Skip to main content

API Reference

Complete reference for Sticky Calls API endpoints.


Base URL

https://api.stickycalls.com

Authentication

All API requests require Bearer token authentication:

Authorization: Bearer YOUR_API_KEY

Get your API key from the dashboard.


Endpoints

POST /v1/calls/start

Start a new call and identify the caller.

Request:

curl -X POST https://api.stickycalls.com/v1/calls/start \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"call_id": "call_001",
"identity_hints": {
"ani": "+14155551234",
"external_ids": {
"customer_id": "12345"
}
}
}'

Request Body Parameters:

FieldTypeRequiredDescription
call_idstringYesUnique identifier for this call (max 128 chars)
identity_hintsobjectYesCaller identification information
identity_hints.anistringNo*Caller's phone number in E.164 format
identity_hints.dnisstringNoDialed number (your phone number they called)
identity_hints.external_idsobjectNo*Key-value pairs of external IDs (CRM ID, account number, etc.)
identity_hints.customer_refstringNo*Previously returned customer_ref UUID
telcoobjectNoTelco lookup data for better confidence scoring
telco.line_typeenumNoOne of: mobile, landline, voip, unknown

*At least one of ani, external_ids, or customer_ref must be provided.

Response (Returning Customer):

{
"call_id": "call_001",
"customer_ref": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"call_start": "2026-02-07T10:30:00.000Z",
"identity": {
"confidence": 0.9,
"level": "very_high",
"sources": ["ani:mobile", "external_id", "recency:1day"],
"recommendation": "reuse"
},
"open_intents": [
{
"intent": "refund_request",
"status": "open",
"attempt_count": 2
}
],
"variables": {
"name": {
"value": "John Doe",
"source": null,
"ttl_seconds": 2592000
},
"email": {
"value": "john@example.com",
"source": null,
"ttl_seconds": 2592000
},
"account_balance": {
"value": "$1,234.56",
"source": null,
"ttl_seconds": 2592000
}
}
}

Response (New Customer):

{
"call_id": "call_001",
"customer_ref": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"call_start": "2026-02-07T10:30:00.000Z",
"identity": {
"confidence": 0,
"level": "low",
"sources": [],
"recommendation": "ignore"
},
"open_intents": [],
"variables": {}
}

Response Fields:

FieldTypeDescription
call_idstringEcho of the request call_id
customer_refstringUUID for this customer (use in /calls/end)
call_startstringISO 8601 timestamp when call started
identity.confidencenumber0.0 to 1.0 confidence score
identity.levelenumlow, medium, high, or very_high
identity.sourcesarrayConfidence factors (e.g., ["ani:mobile", "external_id"])
identity.recommendationenumreuse (≥0.5), confirm (0.3-0.5), or ignore (<0.3)
open_intentsarrayUnresolved intents from previous calls
variablesobjectCustomer context (name, preferences, account info, etc.)

Confidence Recommendations:

  • reuse (≥0.5): Automatically use customer context without verification
  • confirm (0.3-0.5): Ask customer to verify identity before using context
  • ignore (<0.3): Treat as new customer, don't use context

POST /v1/calls/end

End a call and save context for future interactions.

Request (Simple Form):

curl -X POST https://api.stickycalls.com/v1/calls/end \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"call_id": "call_001",
"customer_ref": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"intent": "billing_inquiry",
"intent_status": "resolved",
"variables": {
"name": "John Doe",
"email": "john@example.com",
"last_interaction": "Resolved billing issue - refunded $50"
}
}'

Request Body Parameters (Simple Form):

FieldTypeRequiredDescription
call_idstringYesMust match call_id from /calls/start
customer_refstringNo*UUID from /calls/start response
identity_hintsobjectNo*Same structure as /calls/start
intentstringNoPrimary topic discussed (max 128 chars)
intent_statusenumNoopen (unresolved) or resolved (completed)
variablesobjectNoKey-value pairs to save (keys max 128 chars, values max 1024 chars)

*Either customer_ref or identity_hints must be provided.

Request (Advanced Form with Multiple Intents):

{
"call_id": "call_001",
"customer_ref": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"intent_updates": [
{
"intent": "refund_request",
"status": "resolved",
"resolution": {
"type": "processed",
"external_reference": "refund_12345"
}
},
{
"intent": "account_upgrade",
"status": "open"
}
],
"variable_updates": {
"name": {
"value": "John Doe",
"source": "agent_collected",
"ttl_seconds": 7776000
},
"vip_tier": {
"value": "Gold",
"source": "crm_sync",
"ttl_seconds": 2592000
}
}
}

Response:

{
"call_id": "call_001",
"customer_ref": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"call_start": "2026-02-07T10:30:00.000Z",
"call_end": "2026-02-07T10:35:30.000Z",
"duration_seconds": 330,
"intents_updated": 2,
"variables_updated": 5
}

Response Fields:

FieldTypeDescription
call_idstringEcho of the request call_id
customer_refstringCustomer UUID
call_startstringWhen call started (from /calls/start)
call_endstringWhen call ended
duration_secondsnumberCall duration in seconds
intents_updatednumberNumber of intents saved/updated
variables_updatednumberNumber of variables saved/updated

GET /v1/health

Health check endpoint (no authentication required).

Request:

curl https://api.stickycalls.com/v1/health

Response:

{
"status": "healthy",
"timestamp": "2026-02-07T10:30:00.000Z",
"database": "connected"
}

Error Codes

CodeMeaningWhat To Do
400Bad Request - Invalid parametersCheck request body matches schema
401Unauthorized - Invalid or missing API keyVerify API key is correct and not revoked
402Payment Required - Insufficient creditsAdd credits in dashboard
409Conflict - Duplicate call_id or already endedUse unique call_id for each call
429Too Many Requests - Rate limit exceeded (10% grace period used)Wait for retryAfter seconds, implement exponential backoff
500Internal Server Error - Server errorRetry, contact support if persists

Error Response Format:

{
"error": "Bad Request",
"message": "Validation failed",
"details": {
"issues": [
{
"path": ["identity_hints"],
"message": "identity_hints must include ani, external_ids, or customer_ref"
}
]
}
}

Rate Limit Error (429) Response:

{
"error": "Too Many Requests",
"message": "Rate limit exceeded for your tier. Limit: 600 requests/minute.",
"retryAfter": 42,
"currentUsage": 661,
"limit": 600,
"resetAt": "2026-02-07T10:31:00.000Z"
}

Rate Limit Response Fields:

  • retryAfter: Seconds to wait before retrying
  • currentUsage: Your current request count this minute
  • limit: Your tier's base limit (before grace period)
  • resetAt: When the rate limit window resets

Rate Limits

Rate limits vary by tier with a 10% grace period before hard blocking:

TierLimit (per minute)Grace Period (10%)
Free100 requests110 requests
Starter600 requests660 requests
Growth3,000 requests3,300 requests
Scale10,000 requests11,000 requests

How it works:

  • ⏱️ Rate limits reset every 60 seconds
  • ⚡ 10% grace period allows brief burst traffic without blocking
  • 📊 Rate limit headers included in every response
  • 🚫 Hard block after grace period exceeded

Rate Limit Headers:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 523
X-RateLimit-Reset: 1675789200000

Best Practice: Implement exponential backoff for 429 responses.

View pricing →


Credits & Billing

Credit-based billing:

  • API calls are charged per request
  • Failed requests (4xx, 5xx) are not charged

Free Tier:

  • 100 credits on signup
  • Test keys have separate credit balance

Paid Tiers: See Pricing


Idempotency

Use the Idempotency-Key header for safe retries on /calls/end:

curl -X POST https://api.stickycalls.com/v1/calls/end \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: end_call_001_attempt1" \
-H "Content-Type: application/json" \
-d '{"call_id": "call_001", ...}'
  • Same idempotency key returns cached response (no duplicate save)
  • Keys expire after 24 hours
  • Only applicable to /calls/end
  • Recommended for production use

Best Practices

✅ DO:

  • Always call /calls/start at beginning of call
  • Always call /calls/end at end of call
  • Send external_ids when available (boosts confidence to 0.9+)
  • Use unique call_id for each call (timestamp + random recommended)
  • Use same customer_ref for same customer across calls
  • Interpret confidence correctly (reuse ≥0.5, confirm 0.3-0.5, ignore <0.3)
  • Save meaningful variables (name, preferences, account details)
  • Mark intents status: "open" if unresolved
  • Use Idempotency-Key header for /calls/end
  • Implement exponential backoff for 429 errors

❌ DON'T:

  • Don't skip /calls/start - you'll miss returning customer context
  • Don't skip /calls/end - context won't be saved for next call
  • Don't reuse call_id within 30 days - violates uniqueness constraint
  • Don't save sensitive data (SSN, credit card numbers, passwords)
  • Don't expose confidence scores to customers ("I'm 70% sure you're John")
  • Don't mark intents status: "open" if they're resolved
  • Don't use different customer_ref for same customer

Code Examples

See Quick Start Guide for complete code examples in:

  • Node.js / TypeScript
  • Python
  • Bash / curl

Need Help?


Ready to build? Sign up now →