AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

External Layer

skill-voicenterteam-claude-marketplace-external-layer · by VoicenterTeam

Build an External Layer IVR endpoint that lets Voicenter route inbound calls based on your CRM business logic

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-voicenterteam-claude-marketplace-external-layer

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-voicenterteam-claude-marketplace-external-layer)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of External Layer? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

> 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 AskUserQuestion option 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 implement an External Layer endpoint — a URL that Voicenter's IVR calls mid-flow, so the developer's server can decide where to route each incoming call dynamically based on CRM data.

When to use this skill

Use this skill when the user wants to:

  • Route VIP customers directly to their account manager, bypassing the general queue
  • Route callers with open support tickets directly to the assigned agent
  • Show a custom greeting (SAY_DIGITS) using the caller's name fetched from CRM
  • Route calls differently based on time of day, agent availability, or business rules in their system
  • Pass CRM data (customer ID, tier, open ticket) into the call for use by Pop-Up Screen and CDR Notification
  • Build smart IVR routing without reprogramming the Voicenter IVR layers

Environment Variables

# No outbound API token needed — Voicenter sends requests TO your server.
# Configure your endpoint URL in: Voicenter CPanel → Incoming → IVR → select layer → Layer Settings → "Allow mini external IVR"

How it works

  1. An inbound call reaches a Voicenter IVR layer configured with "Allow Mini External IVR" and your endpoint URL.
  2. Voicenter POSTs the call details (caller ID, DTMF input, layer info) to your endpoint.
  3. Your server queries its CRM/database and responds with a routing action.
  4. Voicenter executes the action immediately.

Critical: Respond within 5 seconds or Voicenter times out and uses the configured fallback layer.

Request Voicenter sends you

{
  "METHOD": "IVR_LAYER_INPUT",
  "DATA": {
    "DID": "0722776772",
    "CALLER_ID": "0501234567",
    "IVR_UNIQUE_ID": "1bcd7954224861f85a2d70612f2",
    "DTMF": "1234",
    "LAYER_ID": "5",
    "PREVIOUS_LAYER_ID": "5"
  }
}

| Field | Description | |---|---| | DID | The virtual number (DID) the caller dialed | | CALLER_ID | Caller's phone number — use this to look them up in your CRM | | IVR_UNIQUE_ID | Unique call ID — correlates with CDR Notification and Pop-Up Screen | | DTMF | Digits the caller pressed. Default "0" if no input collected. | | LAYER_ID | Current IVR layer ID | | PREVIOUS_LAYER_ID | Previous IVR layer ID |

Response Actions

GOTOLAYER — Route to a Voicenter IVR layer

{
  "STATUS": 0,
  "ACTION": "GO_TO_LAYER",
  "LAYER": 12
}

With CRM context (appears in Pop-Up Screen and CDR Notification):

{
  "STATUS": 0,
  "ACTION": "GO_TO_LAYER",
  "LAYER": 22,
  "CALLER_NAME": "John Doe",
  "CUSTOM_DATA": {
    "CRM_client_ID": "12345",
    "Last_ticket_ID": "222",
    "Last_representative": "Jane Smith"
  }
}

SAY_DIGITS — Announce dynamic data then route

{
  "STATUS": 0,
  "ACTION": "SAY_DIGITS",
  "NEXT_LAYER": 2,
  "LANGUAGE": "EN",
  "DATA": [
    { "RecordType": "Recording", "Content": "greeting_audio_file.mp3" },
    { "RecordType": "Digits", "Content": "0501234567" },
    { "RecordType": "Number", "Content": "42" },
    { "RecordType": "Date", "Content": "2024-06-01" },
    { "RecordType": "DateTime", "Content": "2024-06-01T10:00:00" }
  ]
}

LANGUAGE options: HE, EN, AR, RU (more available on request). RecordType options: Recording (play audio file), Digits (digit-by-digit), Number, Date, DateTime.

DIAL — Call an external phone number or extension directly

{
  "STATUS": 0,
  "ACTION": "DIAL",
  "CALLER_ID": "0722776772",
  "CALLER_NAME": "Voicenter",
  "MAX_CALL_DURATION": 1800,
  "MAX_DIAL_DURATION": 60,
  "NEXT_VO_ID": 15,
  "RECORDING": "yes",
  "TARGETS": [
    { "TYPE": "PHONE", "TARGET": "0501234567" }
  ],
  "CUSTOM_DATA": {
    "CRM_client_ID": "12345"
  }
}

TARGET TYPE: PHONE or EXTENSION (Voicenter SIP code). International numbers require country prefix.

Error Response — triggers configured fallback layer

{ "STATUS": 1 }

TypeScript Implementation (Express)

import express, { Request, Response } from 'express';

const app = express();
app.use(express.json());

// Map your Voicenter IVR layer IDs here
const LAYERS = {
  SALES: 10,
  SUPPORT: 11,
  VIP: 12,
  GENERIC: 1,
};

interface ExternalLayerRequest {
  METHOD: string;
  DATA: {
    DID: string;
    CALLER_ID: string;
    IVR_UNIQUE_ID: string;
    DTMF: string;
    LAYER_ID: string;
    PREVIOUS_LAYER_ID: string;
  };
}

app.post('/webhooks/voicenter/external-layer', async (req: Request, res: Response) => {
  const { DATA } = req.body as ExternalLayerRequest;

  try {
    const contact = await crm.findByPhone(DATA.CALLER_ID);

    if (!contact) {
      // Unknown caller → generic queue
      return res.json({ STATUS: 0, ACTION: 'GO_TO_LAYER', LAYER: LAYERS.GENERIC });
    }

    // VIP → skip queue, route directly to account manager
    if (contact.tier === 'VIP') {
      return res.json({
        STATUS: 0,
        ACTION: 'GO_TO_LAYER',
        LAYER: LAYERS.VIP,
        CALLER_NAME: contact.name,
        CUSTOM_DATA: {
          CRM_client_ID: contact.id,
          Account_manager: contact.accountManager,
        },
      });
    }

    // Has open ticket with assigned agent → dial directly
    if (contact.openTicket?.assignedExtension) {
      return res.json({
        STATUS: 0,
        ACTION: 'DIAL',
        CALLER_ID: DATA.DID,
        CALLER_NAME: contact.name,
        MAX_CALL_DURATION: 1800,
        MAX_DIAL_DURATION: 45,
        NEXT_VO_ID: LAYERS.SUPPORT,
        RECORDING: 'yes',
        TARGETS: [{ TYPE: 'EXTENSION', TARGET: contact.openTicket.assignedExtension }],
        CUSTOM_DATA: { CRM_client_ID: contact.id, Ticket_ID: contact.openTicket.id },
      });
    }

    // Known contact → support queue with CRM context
    return res.json({
      STATUS: 0,
      ACTION: 'GO_TO_LAYER',
      LAYER: LAYERS.SUPPORT,
      CALLER_NAME: contact.name,
      CUSTOM_DATA: { CRM_client_ID: contact.id },
    });

  } catch (err) {
    console.error('External layer error:', err);
    return res.json({ STATUS: 1 }); // Voicenter uses configured fallback layer
  }
});

app.listen(3000);

CPanel Configuration

| Setting | Description | |---|---| | MiniExternalDTMFLen | Max DTMF digits to collect before calling your endpoint (e.g. 8 for an 8-digit account ID). Set to 0 if no DTMF needed. | | Delay | Seconds to wait for DTMF input after the prompt plays (1–9). | | MiniExternalDefaultMethodData | Fallback layer ID — always set this. It is used when your endpoint is unreachable or returns STATUS: 1. |

Tips

  • Respond within 5 seconds or Voicenter times out and uses the fallback layer.
  • Normalize CALLER_ID — Israeli numbers may arrive without country prefix. Add 972 prefix if missing and strip the leading 0.
  • Use CUSTOM_DATA to carry CRM context (customer ID, tier, open ticket ID) through the entire call — it appears in CDR Notification and Pop-Up Screen payloads.
  • For DTMF-driven flows ("press 1 for Sales"), read DATA.DTMF and map it to the appropriate layer.

Related Skills

  • Pop-Up Screen — Receives CUSTOM_DATA you pass here to display caller info to the agent
  • CDR NotificationCUSTOM_DATA also appears in the CDR after the call ends
  • VoiceBot API — Combine with External Layer: External Layer runs at call start, VoiceBot runs mid-conversation
  • Active Calls — Check queue depth before routing to decide between queues

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.