CDR & Asterisk Integration¶
How Asterisk stores call data and how AstraPBX processes it.
CDR table schema¶
Asterisk writes one row per channel to asterisk_cdr via ODBC:
| Field | What it stores |
|---|---|
calldate | Channel creation time |
src | Caller number |
dst | Destination as dialed |
dcontext | Dialplan context (key for direction classification) |
channel | Caller's PJSIP channel name |
dstchannel | Destination's PJSIP channel name |
lastapp | Last app executed (Dial, Queue, Playback, Hangup) |
lastdata | Arguments to lastapp |
duration | Total seconds (ring + talk) |
billsec | Talk seconds only (matches recording length) |
disposition | ANSWERED / NO ANSWER / BUSY / FAILED |
accountcode | Organisation UUID |
uniqueid | Unique channel ID |
linkedid | Groups all legs of one call |
recordingfile | MixMonitor filename |
userfield | HANGUPCAUSE\|CHANNEL(hangupsource) |
hangup_reason | Numeric hangup cause code |
queue_name | Queue name (set after Queue() app) |
queue_wait_time | Seconds caller waited in queue |
answered_agent | Agent endpoint that answered |
CDR pipeline¶
1. Call ends → Asterisk writes to asterisk_cdr (ODBC, automatic)
2. AMI Cdr event → asteriskManager.js writes to call_records (app table)
3. CDR poller (every 30s) → reads new rows from asterisk_cdr
→ classifies direction (inbound/outbound/internal)
→ finds org_id from accountcode or channel name
→ POSTs inbound calls to LogsUpdate /auto-ticket/{orgId}
4. GET /api/v1/calls → reads directly from asterisk_cdr
Direction classification¶
channel includes 'trunk' AND src >= 7 digits → inbound
dcontext includes 'outbound' → outbound
dcontext ends with '_incoming' → inbound (fallback)
otherwise → internal
One call, multiple CDR rows¶
A single phone call can produce 2-4 CDR rows (one per channel leg). The API deduplicates by linkedid, keeping the longest-duration row.
| Call type | Rows | Channel pattern |
|---|---|---|
| Direct (A → B) | 1 | PJSIP/endpoint |
| Queue call | 2-3 | Local/ channels + PJSIP |
| Click-to-call | 2 | Local/ + PJSIP/trunk |
| AI outbound | 2 | Manual insert + PJSIP/trunk auto-CDR |
Hangup handler¶
The dialplan hangup handler (h extension) stores:
CDR(userfield) = ${HANGUPCAUSE}|${CHANNEL(hangupsource)}
CDR(hangup_reason) = ${HANGUPCAUSE}
CDR(organization_id) = ${ORG_ID}
Common hangup causes: - 16 — Normal Clearing (one party hung up) - 17 — User Busy - 18 — No User Responding (ring timeout) - 19 — No Answer - 21 — Call Rejected
Config deployment¶
POST /api/v1/config/deploy generates per-org Asterisk files:
pjsip_<orgname>.conf— PJSIP endpoints, auth, AORs for all users + trunksext_<orgname>.conf— dialplan contexts (internal, incoming, outbound, queue, IVR)queues_<orgname>.conf— queue definitions and members
Files written to /etc/asterisk/ and Asterisk reloaded via AMI (core reload).
Trunk generator
Per-org trunks do NOT create IP-based identify rules. Only the system-level tata_gateway endpoint uses IP matching (10.10.10.2). This prevents collisions where org trunks steal inbound Tata calls.
Storage capacity¶
At 500 bytes/row: - 18K calls/day (30 clients) → 270 MB/month → 3.2 GB/year - 75 GB disk holds ~23 years of CDR data - Recordings are the bottleneck, not CDR (moved to GCS hourly)