一键导入
cdr-notification
Receive and handle CDR push notifications from Voicenter after every call ends
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Receive and handle CDR push notifications from Voicenter after every call ends
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Designs the structural skeleton of a Voicenter Bot via interview. Use this skill when the user wants to create, design, scope, or modify a Voicenter voice/chat bot — phrases like "design a bot", "create an agent spec", "build a Voicenter bot", "patch this bot", "add an intent", "change the bot's persona", "modify the flow graph", or any reference to the Agent Spec Designer / Skill 1 in the Voicenter bot generation pipeline. Produces an Agent Spec markdown file (sections 1-4, 4.5, section 5 stubs, section 6 initial, section 7 init). Two named entry modes — greenfield (no spec attached) and patch (spec attached). Does NOT author per-intent language content (validationPrompt, post-execution intentInstructions) — that's Skill 2 (Intent Detail Author). Does NOT emit wire-format JSON — that's Skill 3 (JSON Assembler).
Assembles a fully-detailed Voicenter Agent Spec into Bot JSON wire format — the final mechanical step in the three-skill pipeline. Use this skill when an Agent Spec exists with all section 5 entries marked `[detailed]` and the user wants the deployable JSON. Trigger phrases include "run Skill 3", "assemble the JSON", "emit the bot JSON", "publish the bot", "build the wire-format", "Skill 3 (JSON Assembler)", or any direct continuation from Skill 2's completion handoff. Produces a single `bot-<name>-<date>.json` file plus a banner identifying every fail-loud sentinel and any drift between spec section 6 and what Skill 3 regenerated. Refuses to assemble if any intent is still `[structural]` or `[detailed-revisit]`, or if the spec deviates from the strict template (Doc 2 §3.7). Runs the §15.4 cross-reference pass — 23 checks (8 §15.4 + 3 Compass + 3 botIntents-role + 1 duplicate-global-intent + 8 field-placement doctrine), checks 1–7, 11–13, 15, and 16–21 blocking. Does NOT author any text content (Skills 1 and
Authors the per-intent language content of a Voicenter Agent Spec — slot descriptions, validationPrompt, post-execution intentInstructions, and RT-specific Configuration text. Use this skill when an Agent Spec exists with section 5 entries marked `[structural]` or `[detailed-revisit]`, and the user wants to fill them in. Trigger phrases include "run Skill 2", "detail the intents", "fill in the per-intent fields", "Skill 2 (Intent Detail Author)", or any direct continuation from Skill 1's handoff hint. Walks intents in user-confirmed batches with a checkpoint after each batch. Reactivable — invoke as many times as needed; spec state is the resume point. Does NOT modify the structural skeleton (sections 1, 2, 3, 4, 4.5.1/.2/.4) — that's Skill 1 (Agent Spec Designer). Does NOT emit wire-format JSON — that's Skill 3 (JSON Assembler).
Pull call detail records (CDR) from Voicenter using the Call Log API
Initiate or terminate outgoing calls using the Voicenter Click2Call API
Implement a Pop-Up Screen endpoint that Voicenter calls during incoming calls to display caller CRM data to agents
| name | cdr-notification |
| description | Receive and handle CDR push notifications from Voicenter after every call ends |
Language. Reply in the user's language: detect what they write — Hebrew→Hebrew, English→English — and mirror it, switching if they switch mid-conversation. This shapes your prose, your questions, and your
AskUserQuestionoption labels only. It does not change the artifacts you produce — identifiers, JSON keys, BCP-47 language codes, API field names, and other data stay exactly as specified.
CRM integration context. When the user is building a full CRM integration — not just this API in isolation — always invoke the
crm-onboardingskill alongside this one and frame the answer covering all three core services: Click2Call, Screen Pop, and Call History.
Help the developer implement a CDR Notification endpoint — a webhook that Voicenter POSTs to after every call ends, containing full call detail records including optional AI analysis.
Use this skill when the user wants to:
# No outbound API token needed — Voicenter sends requests TO your server.
# Configure your webhook URL in: Voicenter CPanel → Integrations → CDR Notification
{"Err": 0, "Errdesc": "OK"} immediately.| Field | Description |
|---|---|
direction | incoming / outgoing / internal |
extenUser | Agent's SIP extension code (empty if call never reached an agent) |
callerPhone | Client's caller ID |
isAnswer | 1 = answered, 0 = not answered |
actualCallDuration | Talk time in seconds (excludes ringing) |
actualCallDialtime | Ringing duration in seconds |
caller | Phone number shown to destination |
target | Call destination (phone or SIP) |
time | Call start time (Epoch, Israel timezone) |
duration | Total call duration in seconds (for queue calls includes wait time) |
ivruniqueid | Unique call ID — use to correlate with Pop-Up, Call Log, Real-Time |
type | Call type (see table below) |
status | ANSWER, ABANDONE, NOANSWER, CANCEL, BUSY, TE, etc. |
did | DID (virtual number) dialed — only on incoming calls |
queueid / queuename | Queue details (if call went through a queue) |
record | URL to the call recording MP3 |
price | Call cost in ILS cents (Agorot) |
dialtime | Ringing duration in seconds |
representative_name | Agent's display name |
representative_code | Agent's user ID |
target_country / caller_country | Country names |
seconds_waiting_in_queue | Queue wait time in seconds (queue calls only) |
OriginalIvrUniqueID | Original call ID (for transferred calls) |
DepartmentName / DepartmentID | Account/department info |
IVR | Array of IVR layers the call traversed (layer ID, name, DTMF pressed) |
aiData | Full AI analysis (see below — only if AI is enabled on your account) |
| Type Name | Description |
|---|---|
Incoming Call | Inbound call that ended in IVR or extension (not a queue) |
Queue | Inbound call received by a queue |
Extension Outgoing | Manual outgoing call from extension |
Click2Call leg1 | Click2Call — Leg 1 (connection to agent) |
Click2Call leg2 | Click2Call — Leg 2 (connection to customer, has recording) |
ProductiveCall Leg1/Leg2 | Agent Auto Dialer calls |
Click 2 IVR / Click 2 IVR Incoming | Predictive Dialer calls |
XferCDR | Manually transferred call |
VoiceMail | Call answered by voicemail |
ANSWER, NOANSWER, BUSY, CANCEL, ABANDONE, TIMEOUT, FULL, EXIT, JOINEMPTY, VOEND, TE, NOTCALLED, VOICEMAIL
{
"caller": "0722776772",
"target": "0501234567",
"time": 1595960350,
"duration": 11,
"ivruniqueid": "2020072818dcDHFJcc804",
"type": "Extension Outgoing",
"status": "ANSWER",
"callerextension": "SIPSIP",
"did": "",
"queueid": 0,
"queuename": "",
"record": "https://cpanel.voicenter.co.il/CallsHistory/PlayRecord/2020072818dcDHFJcc804-aws.mp3",
"price": 0,
"dialtime": 2,
"representative_name": "John Doe",
"representative_code": "12345678",
"DepartmentID": 12345678,
"DepartmentName": "Voicenter Sales"
}
{
"caller": "0501234567",
"target": "",
"time": 1595333610,
"duration": 20,
"ivruniqueid": "202007211213270124c",
"type": "Queue",
"status": "ANSWER",
"did": "0722776772",
"queueid": 123456789,
"queuename": "Sales Queue",
"record": "https://cpanel.voicenter.co.il/CallsHistory/PlayRecord/202007211213270124c-aws.mp3",
"seconds_waiting_in_queue": 20,
"IVR": [
{ "layer_id": 1234, "layer_name": "Main IVR", "layer_number": 0, "Dtmf": 2, "dtmf_order": 1 },
{ "layer_id": 4321, "layer_name": "Sales Department", "layer_number": 2, "Dtmf": 0, "dtmf_order": 2 }
]
}
When AI analysis is enabled on the account, aiData contains:
{
"aiData": {
"insights": {
"questions": [
{ "key": "category", "question": "Categorize the call...", "data_type": "string", "answer": "technical issue" },
{ "key": "issue_resolved", "question": "Was the issue resolved?", "data_type": "boolean", "answer": "false" }
],
"participants": {
"caller": { "name": null, "personality_traits": [] },
"callee": { "name": "Steve", "personality_traits": ["professional", "patient"] }
},
"summary": "The caller reported dropped calls. The agent ran through troubleshooting steps..."
},
"emotions": {
"sentences": [
{ "sentence_id": 21, "emotion": "frustrated", "emotion_direction": -1, "confidence_emotion": 0.92, "intensity_emotion": 0.85 }
]
},
"transcript": [
{ "speaker": "Speaker0", "text": "Good afternoon, thank you for calling support", "startTime": 1.87, "endTime": 5.2, "sentence_id": 0 },
{ "speaker": "Speaker1", "text": "Hi, I'm having issues with my phone service", "startTime": 13.46, "endTime": 16.6, "sentence_id": 1 }
]
}
}
Speaker0 = agent (callee), Speaker1 = customer (caller)emotion_direction: 1 = positive, -1 = negative, 0 = neutraldata_type values: 1=boolean, 2=string, 3=number, 4=json array, 5=json object list, 6=json objectimport express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
interface CdrPayload {
ivruniqueid: string;
caller: string;
target: string;
type: string;
status: string;
duration: number;
actualCallDuration: number;
record: string;
representative_name: string;
representative_code: string;
did: string;
queuename: string;
seconds_waiting_in_queue?: number;
IVR?: Array<{ layer_id: number; layer_name: string; Dtmf: number }>;
aiData?: {
insights?: { summary: string; questions: Array<{ key: string; answer: unknown }> };
transcript?: Array<{ speaker: string; text: string; startTime: number }>;
emotions?: { sentences: Array<{ emotion: string; emotion_direction: number }> };
};
}
app.post('/webhooks/voicenter/cdr', async (req: Request, res: Response) => {
const cdr = req.body as CdrPayload;
// 1. Acknowledge immediately — Voicenter needs a fast response
res.json({ Err: 0, Errdesc: 'OK' });
// 2. Process asynchronously after responding
setImmediate(async () => {
try {
// Deduplicate — Voicenter may retry on network errors
const exists = await db.calls.findOne({ callId: cdr.ivruniqueid });
if (exists) return;
// Save CDR to database
await db.calls.create({ callId: cdr.ivruniqueid, ...cdr });
// Missed queue call → trigger SMS callback offer
if (cdr.status === 'ABANDONE' && cdr.queuename) {
await sendSms(cdr.caller, 'We missed your call. We will call you back shortly.');
}
// Save AI analysis if available
if (cdr.aiData?.insights?.summary) {
await db.callInsights.create({
callId: cdr.ivruniqueid,
summary: cdr.aiData.insights.summary,
questions: cdr.aiData.insights.questions,
transcript: cdr.aiData.transcript,
});
}
} catch (err) {
console.error('CDR processing failed:', err);
}
});
});
app.listen(3000);
{ "Err": 0, "Errdesc": "OK" }
| Err | Meaning |
|---|---|
| 0 | OK |
| 1 | Parse error |
| 2 | Application error |
200 OK. Use setImmediate, a queue job, or background worker for heavy processing.ivruniqueid as a unique key and skip duplicates.record field is a direct MP3 link. Store it in your CRM for playback.status: ABANDONE + queuename set = customer hung up while waiting. Send them an SMS/WhatsApp callback offer.ivruniqueid from CDR to correlate with the ringing-phase popup