External Integration API
Server-to-server integration endpoints for pushing data from external systems. These endpoints use API key authentication.
Authentication
All v1 endpoints require API key authentication:
Authorization: Bearer YOUR_API_KEYOr:
X-API-Key: YOUR_API_KEYCreate API keys in Settings → API Keys with appropriate permissions.
Import Historical Data
Push historical contact center data for forecasting.
/api/v1/import/historicalAuth: API KeyImport historical contact data.
Required Permission: import:historical
Request Body:
{ "data": [ { "date": "2026-01-15", "interval": "08:00", "volume": 45, "aht": 180, "queue": "sales", "channel": "voice" }, { "date": "2026-01-15", "interval": "08:30", "volume": 52, "aht": 175, "queue": "sales", "channel": "voice" } ]}| Parameter | Type | Required | Description |
|---|---|---|---|
date | string | Required | Date (YYYY-MM-DD) |
interval | string | Required | Interval start time (HH:MM) |
volume | number | Required | Number of contacts |
aht | number | Required | Average handle time in seconds |
queue | string | Optional | Queue or skill name |
channel | string | Optional | Contact channel (voice, chat, email) |
Response (201):
{ "success": true, "imported": 48, "skipped": 0, "errors": []}Import data in batches of up to 10,000 records per request.
Import Employees
Sync employees from your HRIS or identity provider.
/api/v1/import/employeesAuth: API KeyImport or sync employees.
Required Permission: import:employees
Request Body:
{ "data": [ { "email": "john.doe@company.com", "firstName": "John", "lastName": "Doe", "employeeId": "EMP001", "team": "Support", "role": "agent", "skills": ["billing", "technical"], "active": true }, { "email": "jane.smith@company.com", "firstName": "Jane", "lastName": "Smith", "employeeId": "EMP002", "team": "Sales", "role": "supervisor", "skills": ["sales"], "active": true } ], "updateExisting": false}| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Required | Employee email (unique identifier) |
firstName | string | Required | First name |
lastName | string | Required | Last name |
employeeId | string | Optional | External employee ID |
team | string | Optional | Team name (created if doesn't exist) |
role | string | Optional | Role: "admin", "supervisor", "agent" |
skills | array | Optional | Skill names (created if don't exist) |
active | boolean | Optional | Active status |
updateExisting | boolean | Optional | Update if email exists (default false) |
Response (201):
{ "success": true, "created": 5, "updated": 2, "skipped": 0, "errors": []}With updateExisting: false, existing employees (matched by email) are skipped.
Import Agent States
Push real-time agent state changes from your ACD.
/api/v1/import/agent-statesAuth: API KeyPush real-time agent state changes from your ACD/telephony system.
Required Permission: import:agent-states
Request Body:
{ "data": [ { "agentId": "john.doe@company.com", "state": "on_call", "timestamp": "2026-01-29T08:30:00Z", "queue": "sales", "acdStateCode": "TALKING" }, { "agentId": "jane.smith@company.com", "state": "available", "timestamp": "2026-01-29T08:30:05Z", "queue": "support" } ]}| Parameter | Type | Required | Description |
|---|---|---|---|
agentId | string | Required | Agent identifier (email or employee ID) |
state | string | Required | New state (see valid states below) |
timestamp | string | Required | ISO 8601 timestamp |
queue | string | Optional | Queue or skill name |
acdStateCode | string | Optional | Original ACD state code for mapping |
Valid States:
| State | Description |
|-------|-------------|
| available | Ready to take contacts |
| on_call | Handling a contact |
| after_call_work | Post-call tasks |
| break | On break |
| lunch | On lunch |
| meeting | In a meeting |
| training | In training |
| offline | Not logged in |
| unavailable | Logged in but not available |
Response (201):
{ "success": true, "processed": 2, "skipped": 0, "errors": []}Import Deflection Data
Push AI deflection data from your chatbot, IVR, or virtual agent platform. This tracks contacts resolved automatically before reaching a live agent.
/api/v1/import/deflectionAuth: API KeyImport AI deflection data.
Required Permission: import:deflection
Request Body:
{ "data": [ { "date": "2026-03-15", "interval": "08:00", "contactsDeflected": 12, "totalContactsOffered": 45, "queue": "support", "channel": "chat", "deflectionSource": "chatbot" }, { "date": "2026-03-15", "interval": "08:30", "contactsDeflected": 8, "totalContactsOffered": 38, "queue": "support", "channel": "voice", "deflectionSource": "ivr" } ]}| Parameter | Type | Required | Description |
|---|---|---|---|
date | string | Required | Date (YYYY-MM-DD) |
interval | string | Required | Interval start time (HH:MM) |
contactsDeflected | number | Required | Number of contacts resolved by AI |
totalContactsOffered | number | Optional | Total contacts before deflection (optional — joined from historical data if omitted) |
queue | string | Optional | Queue or skill name |
channel | string | Optional | Contact channel: "voice", "chat", "email", or "sms" |
deflectionSource | string | Optional | Source: "chatbot", "ivr", "virtual_agent", or "self_service" |
metadata | object | Optional | Custom metadata (bot name, confidence scores, etc.) |
Response (200):
{ "success": true, "imported": 2, "message": "Successfully imported 2 deflection records"}Import deflection data on the same interval cadence as your historical contact data. Use totalContactsOffered if your chatbot platform tracks total inbound — otherwise ezyWFM joins with your historical ACD data.
Integration Examples
cURL Example
curl -X POST https://ezywfm.com/api/v1/import/historical \ -H "Authorization: Bearer nwfm_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "data": [{ "date": "2026-03-01", "interval": "08:00", "volume": 45, "aht": 180, "queue": "sales", "channel": "voice" }] }'Node.js Example
const response = await fetch('https://ezywfm.com/api/v1/import/historical', { method: 'POST', headers: { 'Authorization': 'Bearer nwfm_your_api_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify({ data: [{ date: '2026-03-01', interval: '08:00', volume: 45, aht: 180, queue: 'sales', channel: 'voice', }] }),});const result = await response.json();console.log(result);Python Example
import requestsresponse = requests.post( 'https://ezywfm.com/api/v1/import/historical', headers={ 'Authorization': 'Bearer nwfm_your_api_key_here', 'Content-Type': 'application/json', }, json={ 'data': [{ 'date': '2026-03-01', 'interval': '08:00', 'volume': 45, 'aht': 180, 'queue': 'sales', 'channel': 'voice', }] })print(response.json())Rate Limits
External API endpoints are rate limited to 1,000 requests per minute per API key.
When rate limited, you'll receive:
{ "error": "Rate limit exceeded", "retryAfter": 30}Wait the specified seconds before retrying.
Error Handling
Partial Success
Some records may fail while others succeed:
{ "success": true, "processed": 95, "skipped": 3, "errors": [ { "index": 12, "error": "Invalid email format", "data": { "email": "invalid-email" } }, { "index": 45, "error": "Unknown state: CUSTOM_STATE", "data": { "agentId": "agent@example.com" } } ]}Common Errors
| Error | Cause | Solution | |-------|-------|----------| | 401 Unauthorized | Invalid or missing API key | Check API key is correct | | 403 Forbidden | Missing permission | Add required permission to key | | 429 Too Many Requests | Rate limited | Wait and retry | | 400 Validation Error | Invalid data format | Check request body format |
Best Practices
- Batch requests: Send multiple records per request (up to limits)
- Handle errors: Process partial successes and retry failures
- Use timestamps: Include accurate timestamps for agent states
- Map states correctly: Ensure ACD states map to valid ezyWFM states
- Monitor usage: Check API key usage in Settings
- Rotate keys: Periodically rotate API keys for security