Call Logs API¶
Enterprise call history API that reads directly from Asterisk's CDR table in MariaDB.
Endpoint¶
Query parameters¶
| Param | Type | Description |
|---|---|---|
limit | int (1-200) | Records per page (default 50) |
offset | int | Pagination offset |
direction | string | inbound / outbound / internal / all |
disposition | string | ANSWERED / NO ANSWER / BUSY / FAILED |
from | string | Filter by caller number (partial match) |
to | string | Filter by destination (partial match) |
date_from | date | YYYY-MM-DD start |
date_to | date | YYYY-MM-DD end |
search | string | Searches src, dst, caller ID |
Response¶
{
"data": [{
"id": 10,
"calldate": "2026-04-12T12:07:13Z",
"src": "919944421125",
"dst": "918065978001",
"disposition": "NO ANSWER",
"duration": 10,
"billsec": 0,
"direction": "inbound",
"rang_extension": "1001",
"answered_by": null,
"answered_type": null,
"wait_time": 10,
"disconnected_by": "caller",
"hangup_cause": "16",
"hangup_reason": "Normal Clearing",
"hangup_source": "PJSIP/tata_gateway-...",
"queue_name": null,
"queue_wait_time": null,
"answered_agent": null,
"recording_url": "/api/v1/calls/10/recording",
"...": "all raw CDR fields also included"
}],
"pagination": { "total": 100, "limit": 50, "offset": 0, "has_more": true }
}
Enriched fields (derived from raw CDR)¶
| Field | Source | Description |
|---|---|---|
rang_extension | Extracted from dstchannel | Which extension the call rang (e.g., 1001) |
answered_by | Same, only when ANSWERED | Who picked up |
answered_type | dstchannel presence check | human / prompt / queue / null |
wait_time | duration - billsec | Ring time in seconds |
disconnected_by | CHANNEL(hangupsource) via userfield | caller / callee / timeout / busy / system |
hangup_cause | SIP cause code from userfield | 16 = Normal, 17 = Busy, 18 = No Response |
hangup_reason | Mapped from cause code | Human-readable string |
queue_name | From CDR queue_name column | Queue the call entered |
queue_wait_time | From CDR column | Seconds caller waited in queue |
answered_agent | From CDR column | Agent endpoint that answered from queue |
CDR pipeline¶
Call happens → Asterisk writes to asterisk_cdr (ODBC)
→ CDR poller (every 30s) picks up new rows
→ Classifies direction, finds org_id
→ POSTs to LogsUpdate /auto-ticket (for ticket creation)
→ GET /api/v1/calls reads directly from asterisk_cdr
accountcode¶
Every CDR row should have accountcode = org_id. Set by dialplan in:
- Internal context (user extensions) ✓
- Incoming DID subroutine ✓
- Outbound context ✓
- Queue context ✓
Without accountcode, the call history query falls back to channel-name heuristics which can miss some call types.
Recordings¶
Looks for the recording file in order: 1. Local disk: /var/spool/asterisk/monitor/{filename} 2. Firebase Storage via rclone (hourly cron moves files there)
Returns audio/wav stream. Only available when billsec > 0 and recording file exists.
Right-to-erasure: deletes from local disk + GCS, clears CDR reference, writes audit log.