BroadNet Engage API
Carrier-grade outbound voice dialer API for campaign management, live call control, compliance, and real-time reporting. MENA-first, built for scale.
Authentication
All API requests require a JWT bearer token obtained via the login endpoint. Tokens expire after 24 hours.
Authorization: Bearer {your_token} on every request. Requests without a valid token return 401 Unauthorized.
Authenticate with email and password to receive a JWT access token and refresh token. The access token is valid for 24 hours.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Required | User email address | |
| password | string | Required | User password |
{
"email": "agent@acme.com",
"password": "••••••••"
}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400,
"user": {
"id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"email": "agent@acme.com",
"full_name": "Layla Ahmed",
"role": "agent",
"tenant_id": "01919a1b-0000-7e4f-8a5b-ffffffffffff"
}
}
{
"type": "https://engage.broadnet.me/errors/invalid-credentials",
"title": "Invalid credentials",
"status": 401,
"detail": "Email or password is incorrect."
}
curl -X POST https://your-domain.com/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"agent@acme.com","password":"secret"}'
Exchange a valid refresh token for a new access token. Use this before the 24-hour access token expires to avoid prompting users to re-login.
| Parameter | Type | Required | Description |
|---|---|---|---|
| refresh_token | string | Required | Refresh token from login response |
{ "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400
}
curl -X POST https://your-domain.com/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"eyJ..."}'
Errors & Status Codes
All errors follow RFC 7807 (Problem Details for HTTP APIs). The response body always contains type, title, status, and detail.
| Status | Meaning | Common Cause |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
400 | Bad Request | Invalid JSON, missing required field, validation failure |
401 | Unauthorized | Missing or expired JWT token |
403 | Forbidden | Insufficient role permissions |
404 | Not Found | Resource does not exist in your tenant |
409 | Conflict | Duplicate resource (e.g., duplicate phone number in DNC) |
422 | Unprocessable | Semantic validation error (e.g., tenant_id in body rejected) |
429 | Rate Limited | Exceeded 2,000 requests/minute per tenant |
500 | Server Error | Internal error — contact support with instance ID |
Pagination
All list endpoints use cursor-based pagination. Pass the next_cursor from a response as the cursor query parameter to fetch the next page.
GET /v1/calls?limit=50&cursor=eyJpZCI6IjAxOTE5...&status=completed
{
"items": [ ... ],
"total": 4821,
"next_cursor": "eyJpZCI6IjAxOTE5YWZiLWE4YjItN2VjZC04ZTVmLWZmZmZmZmZmZmZmZiJ9",
"has_more": true
}
Campaigns
Manage outbound dialing campaigns. A campaign links a contact list, dialing mode, CLI group, and compliance rules.
Returns a paginated list of campaigns for the authenticated tenant. Filter by status, dialing mode, or date range.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | Optional | draft | active | paused | completed |
| dialing_mode | string | Optional | preview | power | progressive | predictive |
| limit | integer | Optional | Page size (default: 20, max: 100) |
| cursor | string | Optional | Pagination cursor from previous response |
{
"items": [
{
"id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Q2 Renewal Drive",
"status": "active",
"dialing_mode": "progressive",
"total_contacts": 12500,
"contacted": 4821,
"connected": 1203,
"abandon_rate": 2.1,
"cli_group_id": "01919a1b-0001-7e4f-8a5b-aaaaaaaaaaaa",
"created_at": "2026-06-01T08:00:00Z",
"started_at": "2026-06-02T09:00:00Z"
}
],
"total": 14,
"next_cursor": null,
"has_more": false
}
curl https://your-domain.com/v1/campaigns?status=active \
-H "Authorization: Bearer {token}"
Create a new outbound campaign. The campaign starts in draft status. Call POST /v1/campaigns/{id}/start to activate dialing.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Display name for the campaign |
| dialing_mode | string | Required | preview | power | progressive | predictive |
| cli_group_id | uuid | Required | CLI group to use for caller ID |
| max_attempts | integer | Optional | Max dial attempts per contact (default: 3) |
| retry_delay_minutes | integer | Optional | Minutes between retries (default: 60) |
| dial_ratio | number | Optional | Lines per agent for power mode (default: 1.5) |
| timezone | string | Optional | IANA timezone (default: Asia/Dubai) |
{
"name": "Q3 Insurance Renewal",
"dialing_mode": "progressive",
"cli_group_id": "01919a1b-0001-7e4f-8a5b-aaaaaaaaaaaa",
"max_attempts": 4,
"retry_delay_minutes": 90,
"dial_ratio": 2.0,
"timezone": "Asia/Dubai"
}
{
"id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Q3 Insurance Renewal",
"status": "draft",
"dialing_mode": "progressive",
"cli_group_id": "01919a1b-0001-7e4f-8a5b-aaaaaaaaaaaa",
"max_attempts": 4,
"retry_delay_minutes": 90,
"dial_ratio": 2.0,
"timezone": "Asia/Dubai",
"total_contacts": 0,
"created_at": "2026-06-12T10:30:00Z"
}
curl -X POST https://your-domain.com/v1/campaigns \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Q3 Insurance Renewal",
"dialing_mode": "progressive",
"cli_group_id": "01919a1b-0001-7e4f-8a5b-aaaaaaaaaaaa"
}'
Transition a campaign from draft or paused to active. Requires at least one contact in the list and at least one logged-in agent assigned to the campaign.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | uuid | Required | Campaign ID |
No request body required.
{
"id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"status": "active",
"started_at": "2026-06-12T11:00:00Z",
"message": "Campaign dialing started."
}
{
"type": "https://engage.broadnet.me/errors/campaign-no-contacts",
"title": "Campaign has no contacts",
"status": 409,
"detail": "Upload contacts before starting the campaign."
}
curl -X POST https://your-domain.com/v1/campaigns/01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b/start \
-H "Authorization: Bearer {token}"
Pause an active campaign. In-progress calls are allowed to complete. No new outbound dials are initiated after this call returns.
No request body required. Pass campaign ID in the URL.
{
"id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"status": "paused",
"paused_at": "2026-06-12T13:45:00Z"
}
curl -X POST https://your-domain.com/v1/campaigns/01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b/pause \
-H "Authorization: Bearer {token}"
Contacts
Upload, search, and manage contact lists. Contacts are always scoped to a campaign. Phone numbers are validated and normalized to E.164 format.
Returns a paginated list of contacts for a specific campaign. Filter by dial status to find pending, contacted, or failed contacts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | Optional | pending | dialing | connected | completed | failed | dnc |
| limit | integer | Optional | Page size (default: 50, max: 200) |
| cursor | string | Optional | Pagination cursor |
{
"items": [
{
"id": "01919cdd-c2d3-7e4f-8a5b-111111111111",
"phone": "+971501234567",
"first_name": "Ahmad",
"last_name": "Al Rashid",
"status": "pending",
"attempts": 0,
"last_call_at": null,
"disposition": null,
"custom_fields": {
"account_number": "ACC-00421",
"renewal_date": "2026-07-15"
}
}
],
"total": 12500,
"next_cursor": "eyJpZCI6IjAxOTE5Y2RkIn0=",
"has_more": true
}
curl "https://your-domain.com/v1/campaigns/01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b/contacts?status=pending&limit=50" \
-H "Authorization: Bearer {token}"
Bulk-upload contacts to a campaign. Supports up to 10,000 contacts per request. Duplicates (same phone number) are silently deduplicated. Phone numbers are validated and normalized to E.164 format.
dnc.| Parameter | Type | Required | Description |
|---|---|---|---|
| contacts | array | Required | Array of contact objects (max 10,000) |
| contacts[].phone | string | Required | Phone number in E.164 or local format |
| contacts[].first_name | string | Optional | Contact first name |
| contacts[].last_name | string | Optional | Contact last name |
| contacts[].custom_fields | object | Optional | Arbitrary key-value metadata shown to agents |
{
"contacts": [
{
"phone": "+971501234567",
"first_name": "Ahmad",
"last_name": "Al Rashid",
"custom_fields": {
"account_number": "ACC-00421",
"policy_type": "comprehensive",
"renewal_date": "2026-07-15"
}
},
{
"phone": "+97144561234",
"first_name": "Sara",
"last_name": "Hassan"
}
]
}
{
"imported": 2,
"duplicates_skipped": 0,
"dnc_filtered": 0,
"invalid_phones": 0,
"total_in_campaign": 12502
}
curl -X POST https://your-domain.com/v1/campaigns/01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b/contacts \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"contacts":[{"phone":"+971501234567","first_name":"Ahmad"}]}'
Full-text search across all contacts in your tenant. Search by phone number, name, or custom field values. Results are ranked by relevance.
{
"q": "Ahmad",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"limit": 20
}
{
"items": [
{
"id": "01919cdd-c2d3-7e4f-8a5b-111111111111",
"phone": "+971501234567",
"first_name": "Ahmad",
"last_name": "Al Rashid",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"campaign_name": "Q3 Insurance Renewal",
"status": "pending"
}
],
"total": 1
}
curl -X POST https://your-domain.com/v1/contacts/search \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"q":"Ahmad","limit":20}'
Calls
Query call history, live call counts, and originate manual calls. All times are in UTC ISO-8601.
Returns paginated call history with rich filtering. Supports filtering by campaign, agent, date range, direction, and disposition.
| Parameter | Type | Required | Description |
|---|---|---|---|
| campaign_id | uuid | Optional | Filter to a specific campaign |
| agent_id | uuid | Optional | Filter by agent |
| status | string | Optional | completed | no_answer | busy | failed | voicemail |
| from | datetime | Optional | Start of date range (ISO-8601 UTC) |
| to | datetime | Optional | End of date range (ISO-8601 UTC) |
| limit | integer | Optional | Page size (default: 50, max: 200) |
{
"items": [
{
"id": "01919dee-c2d3-7e4f-8a5b-222222222222",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"contact_phone": "+971501234567",
"direction": "outbound",
"status": "completed",
"disposition": "sale",
"duration_seconds": 248,
"talk_time_seconds": 212,
"started_at": "2026-06-12T10:15:00Z",
"answered_at": "2026-06-12T10:15:18Z",
"ended_at": "2026-06-12T10:19:08Z",
"recording_id": "01919eff-aaaa-7e4f-8a5b-333333333333"
}
],
"total": 4821,
"next_cursor": "eyJpZCI6IjAxOTE5ZGVlIn0=",
"has_more": true
}
curl "https://your-domain.com/v1/calls?campaign_id=01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b&status=completed&from=2026-06-01T00:00:00Z" \
-H "Authorization: Bearer {token}"
Returns aggregated call statistics for the current calendar day in the tenant's configured timezone. Useful for dashboard widgets.
No parameters required. Optionally pass ?campaign_id={id} to scope to one campaign.
{
"date": "2026-06-12",
"total_calls": 1842,
"connected": 624,
"no_answer": 810,
"busy": 204,
"failed": 124,
"voicemail": 80,
"avg_talk_time_seconds": 187,
"abandon_rate": 2.3,
"as_of": "2026-06-12T13:45:00Z"
}
curl https://your-domain.com/v1/calls/count-today \
-H "Authorization: Bearer {token}"
Returns the real-time count of active (in-progress) calls. Updated every second from FreeSWITCH ESL. Use this to power live wallboard displays.
No parameters required.
{
"active_calls": 47,
"ringing": 12,
"connected": 35,
"as_of": "2026-06-12T13:45:33Z"
}
curl https://your-domain.com/v1/calls/active-count \
-H "Authorization: Bearer {token}"
Originate a manual outbound call from an agent's SIP endpoint to a destination phone number. A compliance check is always performed before dialing. Returns immediately — call state is delivered via webhooks.
allowed: false, the call is blocked and a 403 is returned with the specific rule that blocked it.| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_id | uuid | Required | Agent who will receive the bridged call |
| destination | string | Required | Destination phone in E.164 format |
| campaign_id | uuid | Optional | Associate call with a campaign for reporting |
| cli_override | string | Optional | Override caller ID (must be in tenant's CLI group) |
{
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"destination": "+971501234567",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b"
}
{
"call_id": "01919dee-c2d3-7e4f-8a5b-999999999999",
"status": "dialing",
"destination": "+971501234567",
"compliance": {
"allowed": true,
"rule_version": "2026-06-01",
"audit_ref": "AUD-9921847"
}
}
{
"type": "https://engage.broadnet.me/errors/compliance-blocked",
"title": "Call blocked by compliance",
"status": 403,
"detail": "Number is on the national DNC registry (TDRA).",
"compliance": {
"allowed": false,
"rule": "dnc_national",
"rule_version": "2026-06-01",
"audit_ref": "AUD-9921848"
}
}
curl -X POST https://your-domain.com/v1/calls/originate \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"destination": "+971501234567",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b"
}'
Agents
Monitor live agent states, change availability, and check SIP registration status for your contact center floor.
Returns the real-time state of all agents in the tenant. Updated within 1 second of state changes via FreeSWITCH ESL events.
No parameters required. Optionally pass ?team_id={id} to filter by team.
{
"agents": [
{
"id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"full_name": "Layla Ahmed",
"state": "on_call",
"state_since": "2026-06-12T13:42:17Z",
"sip_registered": true,
"current_call_id": "01919dee-c2d3-7e4f-8a5b-999999999999",
"calls_today": 42,
"talk_time_today_seconds": 8940
},
{
"id": "01919a1b-c2d3-7e4f-8a5b-aabbccddeeff",
"full_name": "Omar Khalil",
"state": "available",
"state_since": "2026-06-12T13:41:05Z",
"sip_registered": true,
"current_call_id": null,
"calls_today": 38,
"talk_time_today_seconds": 7612
}
],
"summary": {
"total": 24,
"available": 8,
"on_call": 12,
"break": 3,
"offline": 1
}
}
curl https://your-domain.com/v1/agents/state \
-H "Authorization: Bearer {token}"
Allows the authenticated agent to change their own availability state. Supervisors can use POST /v1/agents/{id}/state to change another agent's state.
| Parameter | Type | Required | Description |
|---|---|---|---|
| state | string | Required | available | break | lunch | training | offline |
| reason | string | Optional | Free-text reason (shown to supervisors) |
{ "state": "break", "reason": "Short break" }
{
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"state": "break",
"state_since": "2026-06-12T13:50:00Z",
"reason": "Short break"
}
curl -X POST https://your-domain.com/v1/agents/me/state \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"state":"break","reason":"Short break"}'
Returns which agents currently have an active SIP registration in FreeSWITCH. An agent must be SIP-registered to receive calls.
No parameters required.
{
"registered": [
{
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"sip_user": "agent_1001",
"registered_at": "2026-06-12T08:02:44Z",
"user_agent": "SIP.js/0.21.2"
}
],
"total_registered": 23,
"total_agents": 24
}
curl https://your-domain.com/v1/agents/sip-registered \
-H "Authorization: Bearer {token}"
Compliance & DNC
Pre-dial compliance checks and Do Not Call (DNC) list management. The compliance engine checks calling hours, national DNC registries (UAE TDRA, KSA CITC, Egypt NTRA, Jordan TRC), consent records, and CLI ownership.
allowed: false. The dialer never assumes permission on failure.Run a compliance check before dialing a number. Returns whether the call is permitted along with the governing rule version and an immutable audit reference. The dialer calls this automatically — expose it for CRM integrations or manual dial flows.
| Parameter | Type | Required | Description |
|---|---|---|---|
| phone | string | Required | Phone number to check (E.164) |
| campaign_id | uuid | Optional | Campaign context for calling-hours rules |
| jurisdiction | string | Optional | ISO-3166-1 alpha-2 (auto-detected from phone if omitted) |
{
"phone": "+971501234567",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"jurisdiction": "AE"
}
{
"allowed": true,
"rule_version": "2026-06-01",
"audit_ref": "AUD-9921849",
"checks": {
"calling_hours": "passed",
"dnc_national": "passed",
"dnc_tenant": "passed",
"consent": "passed",
"cli_ownership": "passed"
}
}
{
"allowed": false,
"rule_version": "2026-06-01",
"audit_ref": "AUD-9921850",
"blocking_rule": "calling_hours",
"blocking_reason": "Outside permitted calling hours for AE (09:00–18:00 Sun–Thu).",
"checks": {
"calling_hours": "failed",
"dnc_national": "passed",
"dnc_tenant": "passed",
"consent": "passed",
"cli_ownership": "passed"
}
}
curl -X POST https://your-domain.com/v1/compliance/check \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"phone":"+971501234567","jurisdiction":"AE"}'
Returns the tenant's internal DNC list (numbers added manually or via agent disposition). National registry records are not listed here.
Optional: ?limit=50&cursor={cursor}
{
"items": [
{
"id": "01919fff-c2d3-7e4f-8a5b-444444444444",
"phone": "+971509876543",
"source": "agent_disposition",
"reason": "Customer requested no contact",
"added_by": "Layla Ahmed",
"added_at": "2026-06-10T14:22:00Z"
}
],
"total": 342,
"has_more": true
}
curl https://your-domain.com/v1/dnc?limit=50 \
-H "Authorization: Bearer {token}"
Add one or more phone numbers to the tenant's internal DNC list. Numbers are immediately excluded from all future campaigns. Supports bulk import of up to 5,000 numbers per request.
{
"phones": ["+971509876543", "+97144123456"],
"reason": "Customer opt-out via email"
}
{
"added": 2,
"duplicates_skipped": 0,
"invalid_phones": 0
}
curl -X POST https://your-domain.com/v1/dnc \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"phones":["+971509876543"],"reason":"Customer opt-out"}'
Recordings
Access call recordings. All recordings are stored in MinIO object storage. Playback URLs are signed and expire after 1 hour for security.
Returns a paginated list of call recordings. Filter by campaign, agent, date range, or minimum duration.
| Parameter | Type | Required | Description |
|---|---|---|---|
| campaign_id | uuid | Optional | Filter to campaign |
| agent_id | uuid | Optional | Filter to agent |
| from | datetime | Optional | Start date (ISO-8601 UTC) |
| to | datetime | Optional | End date (ISO-8601 UTC) |
| min_duration | integer | Optional | Minimum duration in seconds |
| limit | integer | Optional | Page size (default: 50) |
{
"items": [
{
"id": "01919eff-aaaa-7e4f-8a5b-333333333333",
"call_id": "01919dee-c2d3-7e4f-8a5b-999999999999",
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"agent_name": "Layla Ahmed",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"contact_phone": "+971501234567",
"duration_seconds": 248,
"format": "mp3",
"size_bytes": 1982464,
"recorded_at": "2026-06-12T10:15:00Z"
}
],
"total": 1203,
"has_more": true
}
curl "https://your-domain.com/v1/recordings?campaign_id=01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b&min_duration=60" \
-H "Authorization: Bearer {token}"
Returns a pre-signed URL for streaming or downloading a recording. The URL expires after 1 hour. Never embed raw storage paths — always use this endpoint to get a fresh signed URL.
No body required. Pass recording ID in the URL.
{
"id": "01919eff-aaaa-7e4f-8a5b-333333333333",
"url": "https://recordings.your-domain.com/01919eff.mp3?X-Amz-Expires=3600&X-Amz-Signature=abc123...",
"expires_at": "2026-06-12T14:45:00Z",
"format": "mp3",
"duration_seconds": 248
}
curl https://your-domain.com/v1/recordings/01919eff-aaaa-7e4f-8a5b-333333333333/play \
-H "Authorization: Bearer {token}"
Reports
Export campaign, agent, and call data in JSON, CSV, or PDF format.
Generate and download a report. For large exports (over 10,000 rows), a download URL is returned instead of inline data. The URL is valid for 30 minutes.
| Parameter | Type | Required | Description |
|---|---|---|---|
| report_type | string | Required | call_detail | campaign_summary | agent_performance | disposition_summary |
| format | string | Required | json | csv | pdf |
| from | datetime | Required | Report start date (ISO-8601 UTC) |
| to | datetime | Required | Report end date (ISO-8601 UTC) |
| campaign_id | uuid | Optional | Filter to campaign |
| agent_ids | array | Optional | Filter to specific agents |
{
"report_type": "agent_performance",
"format": "csv",
"from": "2026-06-01T00:00:00Z",
"to": "2026-06-12T23:59:59Z",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b"
}
{
"report_type": "agent_performance",
"format": "csv",
"rows": 12,
"data": "Agent Name,Calls,Connected,Talk Time (min),Avg Handle Time,Disposition Rate\nLayla Ahmed,42,28,156.2,223,66.7%\n..."
}
{
"report_type": "call_detail",
"format": "csv",
"rows": 45210,
"download_url": "https://exports.your-domain.com/report_2026-06-12_abcdef.csv?X-Amz-Signature=...",
"expires_at": "2026-06-12T14:15:00Z"
}
curl -X POST https://your-domain.com/v1/reports/export \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"report_type": "agent_performance",
"format": "csv",
"from": "2026-06-01T00:00:00Z",
"to": "2026-06-12T23:59:59Z"
}'
Webhooks
BroadNet Engage sends real-time events to your configured endpoint. Register webhook URLs in Admin → Settings → Integrations.
Event Catalog
Payload structure
Every event shares the same envelope:
{
"event_id": "01919fff-1111-7e4f-8a5b-555555555555",
"event_type": "call.completed",
"tenant_id": "01919a1b-0000-7e4f-8a5b-ffffffffffff",
"occurred_at": "2026-06-12T13:50:22Z",
"correlation_id": "01919dee-c2d3-7e4f-8a5b-999999999999",
"schema_version": "1",
"payload": {
"call_id": "01919dee-c2d3-7e4f-8a5b-999999999999",
"agent_id": "01919a1b-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"campaign_id": "01919bcc-c2d3-7e4f-8a5b-6c7d8e9f0a1b",
"contact_phone": "+971501234567",
"status": "completed",
"disposition": "sale",
"duration_seconds": 248,
"recording_id": "01919eff-aaaa-7e4f-8a5b-333333333333"
}
}
Signature verification
Every webhook request includes an X-BroadNet-Signature header. Verify it to ensure the payload is authentic:
import hmac, hashlib
def verify_webhook(payload_bytes: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Retry policy
| Attempt | Delay | Total elapsed |
|---|---|---|
| 1 | Immediate | 0s |
| 2 | 30 seconds | 30s |
| 3 | 5 minutes | 5m 30s |
| 4 | 30 minutes | 35m 30s |
| 5 | 2 hours | 2h 35m |
Your endpoint must return a 2xx status within 10 seconds. Timeouts count as failures and trigger a retry.
event_id. Retried events have the same event_id.SDKs & Libraries
Official SDKs are coming soon. In the meantime, use the REST API directly with any HTTP client.
instance ID from error responses to speed up diagnosis.
© 2026 BroadNet Technologies. All rights reserved. · API v1 · api-support@broadnet.me