CDR status webhook
Webhook push protocol, event payload, and X-CALLBACK-ID signature verification for voice call records.
CDR status webhook
When a call reaches its final state, the platform pushes the call record to your configured webhook endpoint via HTTP POST. This is an alternative to polling the call-record query API.
Prerequisite: Configure a webhook endpoint in the InnoPaas Console (event type: CDR status push, cdr_status_push). Signing credentials (username and secret) are optional; when configured, pushes carry a signature header — see Signature verification.
Push protocol
- Method:
POSTwith anapplication/json; charset=utf-8body - Your receiver must respond with 2xx within the timeout; anything else is treated as a failure
-
The current version does not retry failed pushes. Keep your receiver stable and handle
eventIdidempotently (eventIdequals the call identifiercallId).
Event payload
{
"eventCode": "cdr_status_push",
"eventId": "YYYX931******64224",
"eventTime": "2026-11-09 10:00:41",
"data": {
"callId": "YYYX931******64224",
"mobile": "8613800138000",
"callStatus": 2,
"callDesc": "200",
"callDescMsg": "呼叫成功",
"callTime": "2026-11-09 10:00:05",
"startTime": "2026-11-09 10:00:07",
"answerTime": "2026-11-09 10:00:11",
"byeTime": "2026-11-09 10:00:35",
"ringTime": "2026-11-09 10:00:09",
"invokeTime": "2026-11-09 10:00:00",
"batchNo": "B20261109***",
"templateId": "Tpl_0001",
"recordUrl": "https://example.com/rec/xxx.mp3"
}
}data fields:
| Field | Type | Description |
|---|---|---|
| callId | String | Unique call identifier (same callId returned by the send API) |
| mobile | String | Recipient number |
| callStatus | Integer | Call status code (numeric; refer to the actual push) |
| callDesc | String | Release code |
| callDescMsg | String | Release code description |
| callTime | String | Call time |
| startTime | String | Dial-out time of this call attempt |
| answerTime | String | Answer time (empty when unanswered) |
| byeTime | String | Hang-up time |
| ringTime | String | Ring time |
| invokeTime | String | Task initiation time |
| batchNo | String | Batch number |
| templateId | String | Template ID |
| recordUrl | String | Recording file URL (empty when none) |
Signature verification
When signing credentials are configured, each push carries an X-CALLBACK-ID header:
timestamp=<epoch millis>;nonce=<32-char random>;username=<configured username>;signature=<signature>How signature is computed:
- Signed content =
timestamp + nonce + username, the three segments concatenated in order with no separator - Algorithm =
HMAC-SHA256, keyed by the configured secret - Output is a lowercase hex string
Verification example (Java):
String header = request.getHeader("X-CALLBACK-ID"); // timestamp=...;nonce=...;username=...;signature=...
Map<String, String> kv = new LinkedHashMap<>();
for (String pair : header.split(";")) {
int i = pair.indexOf('=');
kv.put(pair.substring(0, i), pair.substring(i + 1));
}
String expected = hmacSha256Hex(SECRET,
kv.get("timestamp") + kv.get("nonce") + kv.get("username"));
if (!expected.equals(kv.get("signature"))) {
// verification failed, reject
}If no signing credentials are configured (both username and secret empty), the push is a plain POST without the X-CALLBACK-ID header.
Notes
- The endpoint must be publicly reachable; a 2xx response means success
- Handle
eventId(=callId) idempotently to avoid duplicate writes - Push fields align with the record query API — combine them: receive in real time via webhook, reconcile via the query API
Last updated on