一键导入
real-time
Connect to Voicenter real-time call and agent events via the EventsSDK (socket.io)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Connect to Voicenter real-time call and agent events via the EventsSDK (socket.io)
用 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
Receive and handle CDR push notifications from Voicenter after every call ends
Initiate or terminate outgoing calls using the Voicenter Click2Call API
| name | real-time |
| description | Connect to Voicenter real-time call and agent events via the EventsSDK (socket.io) |
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.
Help the developer integrate the Voicenter Real-Time API — a socket.io-based SDK that streams live call events, queue changes, and agent status updates continuously.
Use this skill when the user wants to:
Use Active Calls API instead when you only need on-demand snapshots without a persistent connection.
VOICENTER_TOKEN=your_realtime_token_here
# Token type depends on your login method:
# - Token login: use VOICENTER_TOKEN
# - Account login: use VOICENTER_USERNAME + VOICENTER_PASSWORD
# - User login: use VOICENTER_EMAIL + VOICENTER_PASSWORD
https://cdn.voicenter.co/cdn/events-sdk/voicenter-events-sdk.umd.jshttps://github.com/VoicenterTeam/VoicenterEventsSDK/tree/next/examples/jsVoicenterEventsSDK.NET| Type | Use case | Required credentials |
|---|---|---|
token | Server-side — entire account events | token from Voicenter |
account | Server-side — entire account events | username + password |
user | Per-agent — events for one user/extension | email + password from CPanel |
// Token login (recommended for server-side integrations)
const sdk = new EventsSDK({
loginType: 'token',
useLoginApi: true,
token: process.env.VOICENTER_TOKEN,
});
// Account login
const sdk = new EventsSDK({
loginType: 'account',
username: process.env.VOICENTER_USERNAME,
password: process.env.VOICENTER_PASSWORD,
});
// User login (single agent)
const sdk = new EventsSDK({
loginType: 'user',
email: process.env.VOICENTER_EMAIL,
password: process.env.VOICENTER_PASSWORD,
});
sdk.init();
loginSuccess — Connection confirmedsdk.on('loginSuccess', (response) => {
// { errorCode: 0, errorDesc: "OK", servertime: 1597930812, servertimeoffset: 180 }
console.log('Connected to Voicenter Real-Time');
});
loginStatus — Initial queue state on connectsdk.on('loginStatus', (response) => {
// { errorCode: 0, Queues: [{ QueueID, QueueName, Calls: [...] }] }
response.Queues.forEach(q => {
console.log(`Queue: ${q.QueueName}, waiting: ${q.Calls.length}`);
});
});
AllExtensionsStatus — Full extension snapshot on connectsdk.on('AllExtensionsStatus', (response) => {
response.extensions.forEach(ext => {
console.log(`${ext.userName} (${ext.extenUser}): status=${ext.representativeStatus}, calls=${ext.calls.length}`);
});
});
Extension fields: userID, userName, extenUser, number, onlineUserID, representativeStatus (1–13), calls (active calls array), lastCallEventEpoch, lastAnsweredCallEventEpoch.
QueueEvent — Call enters or exits a queuesdk.on('QueueEvent', (response) => {
const { reason, data, ivruniqueid } = response;
// reason: "JOIN" | "EXIT" | "ABANDONED"
if (reason === 'JOIN') {
console.log(`New call in queue ${data.QueueName}: ${data.Calls[0]?.CallerID}`);
}
if (reason === 'ABANDONED') {
const callerID = data.Calls[0]?.CallerID ?? '';
if (callerID) sendSms(callerID, "We missed your call. We'll call you back!");
}
});
QueueEvent fields: QueueID, QueueName, Calls array of { CallerID, CallerName, IvrUniqueID, JoinTimeStamp }.
ExtensionEvent — Call lifecycle and agent status changessdk.on('ExtensionEvent', (response) => {
const { reason, cause, data } = response;
// reason: "NEWCALL" | "ANSWER" | "HOLD" | "UNHOLD" | "HANGUP" | "userStatusUpdate"
const call = data.currentCall;
switch (reason) {
case 'NEWCALL':
console.log(`${data.userName}: new ${call.direction} call from ${call.callerphone}`);
break;
case 'ANSWER':
console.log(`${data.userName}: answered — ivrid: ${call.ivrid}`);
break;
case 'HANGUP':
console.log(`${data.userName}: call ended (${cause}) — ivrid: ${data.ivruniqueid}`);
break;
case 'userStatusUpdate':
console.log(`${data.userName}: status → ${data.representativeStatus}`);
break;
}
});
ExtensionEvent reasons| Reason | Trigger |
|---|---|
NEWCALL | Incoming ringing or outgoing dialing |
ANSWER | Call answered |
HOLD | Call placed on hold |
UNHOLD | Call taken off hold |
HANGUP | Call ended |
userStatusUpdate | Agent changed status (Login/Logout/Lunch etc.) |
ExtensionEvent HANGUP cause values| Cause | Meaning |
|---|---|
Normal hangup | Call ended normally |
Answered elsewhere | Call rang on multiple extensions, answered by another |
Call Rejected | Click2Call Leg1 was not answered |
currentCall fieldscallStarted, callAnswered, answered (0/1), callerphone, callstatus (Ringing/Dialing/Talking/Hold), direction (Incoming/Outgoing/Click2Call/Spy), ivrid, recording.IsMuted, c2cdirection (1=Leg1, 2=Leg2), did, customdata.
declare const EventsSDK: any;
interface ExtState {
name: string;
status: number;
activeCalls: number;
}
const state = {
extensions: new Map<string, ExtState>(),
queues: new Map<string, { name: string; waiting: number }>(),
};
const sdk = new EventsSDK({
loginType: 'token',
useLoginApi: true,
token: process.env.VOICENTER_TOKEN!,
});
sdk.init().then(() => {
sdk.on('loginSuccess', () => console.log('Connected to Voicenter Real-Time'));
sdk.on('AllExtensionsStatus', (res: any) => {
res.extensions.forEach((ext: any) => {
state.extensions.set(ext.extenUser, {
name: ext.userName,
status: ext.representativeStatus,
activeCalls: ext.calls.length,
});
});
console.log(`Initialized ${state.extensions.size} extensions`);
});
sdk.on('loginStatus', (res: any) => {
res.Queues.forEach((q: any) => {
state.queues.set(String(q.QueueID), { name: q.QueueName, waiting: q.Calls.length });
});
});
sdk.on('QueueEvent', (event: any) => {
const { reason, data } = event;
state.queues.set(String(data.QueueID), { name: data.QueueName, waiting: data.Calls.length });
if (reason === 'ABANDONED') {
const caller = data.Calls[0]?.CallerID;
if (caller) sendSms(caller, "We missed your call. We'll call you back shortly!");
}
});
sdk.on('ExtensionEvent', (event: any) => {
const { reason, data } = event;
const ext = state.extensions.get(data.extenUser);
if (ext) {
ext.status = data.representativeStatus;
ext.activeCalls = data.calls?.length ?? 0;
}
if (reason === 'HANGUP' && data.currentCall?.direction === 'Incoming') {
notifyCRM('call_ended', { ivrid: data.ivruniqueid, ext: data.extenUser });
}
});
});
representativeStatus)| Code | Status |
|---|---|
| 1 | Login |
| 2 | Logout |
| 3 | Lunch |
| 5 | Administrative |
| 7 | Private |
| 9 | Other |
| 11 | Training |
| 12 | Team meeting |
| 13 | Brief |
AllExtensionsStatus + loginStatus on connect, then apply incremental ExtensionEvent and QueueEvent updates.QueueEvent reason: ABANDONED and CDR Notification status: ABANDONE both fire for the same event — deduplicate by ivruniqueid.c2cdirection: 1 = Leg1 (agent connection), c2cdirection: 2 = Leg2 (customer connection).userStatusUpdate, data.calls will be empty — only representativeStatus has changed.userStatusUpdate events to confirm status changes set via the API