Introduction
This recipe shows how to integrate VoxEQ’s voice intelligence with Amazon Connect and Amazon Lex v2 to (a) detect imposters in real time with Verify and (b) personalize virtual-agent responses with Prompt by mapping VoxEQ labels to Lex session attributes for tone and pace control. VoxEQ’s platform is API-first, language-agnostic, and privacy-preserving (no PII or voiceprints stored) and is proven in large-scale contact centers, including integrations with Amazon Connect and a September 2025 partnership with TTEC Digital’s SmartApps Cloud. See: VoxEQ Verify, VoxEQ Prompt, Product Guide, and TTEC Digital + VoxEQ announcement.
Reference architecture
-
Customer calls Amazon Connect.
-
Connect starts real-time media streaming. A Lambda (Kinesis consumer) sends the first ~4–6 seconds of audio to your backend that calls VoxEQ Verify for fraud risk and VoxEQ Prompt for demographic labels.
-
The backend stores a short-lived verdict object, e.g., {riskScore, isSynthetic, labels: {ageGroup, birthSex, heightBand}}.
-
The contact flow invokes a “Poller” Lambda to read the verdict and writes the results into Contact Attributes and Lex session attributes.
-
Connect routes high-risk calls to specialized queues; low-risk calls continue to a Lex v2 bot. The Lex code hook reads the VoxEQ-derived attributes to choose tone, vocabulary, and pacing dynamically.
Why VoxEQ for this pattern
-
Works without enrollment and across languages, returning signals within seconds, which is critical at IVR entry. Verify and Product Guide
-
Detects synthetic/deepfake voices while allowing legitimate synthetic uses like voicemail. Verify
-
Privacy-first: no storage of PII/voiceprints; delivers labels and risk scores. AI Ethics Statement
-
Confirmed ecosystem fit: integrations and partner deployments, including Amazon Connect and TTEC Digital. Home, TTEC + VoxEQ
Prerequisites
-
Amazon Connect instance with real-time media streaming enabled and permissions to invoke Lambdas.
-
Amazon Lex v2 bot/alias integrated with your Connect instance.
-
Two Lambdas:
-
Media consumer (reads audio stream events from Kinesis and calls your backend → VoxEQ APIs).
-
Poller (reads latest verdict and returns attributes to Connect).
-
A lightweight backend (or the media Lambda itself) that calls VoxEQ Verify/Prompt and persists short-lived verdicts.
-
IAM roles for Connect → Lambda and Kinesis → Lambda.
Step 1 — Contact flow (JSON skeleton)
Import or adapt the following minimal flow. It starts media streaming, polls for a VoxEQ verdict, sets attributes, routes high risk, and then invokes a Lex v2 bot with the mapped session attributes.
{
"Version": "2019-10-30",
"StartAction": "start",
"Actions": [
{
"Identifier": "start",
"Type": "StartMediaStreaming",
"Parameters": {"Audio": true},
"Transitions": {"NextAction": "pollVoxEQ", "Errors": [{"NextAction": "fallback"}]}
},
{
"Identifier": "pollVoxEQ",
"Type": "InvokeLambdaFunction",
"Parameters": {"FunctionArn": "arn:aws:lambda:REGION:ACCT:function:VoxEQVerdictPoller"},
"Transitions": {"NextAction": "setAttributes", "Errors": [{"NextAction": "fallback"}], "TimeLimit": {"NextAction": "fallback"}}
},
{
"Identifier": "setAttributes",
"Type": "UpdateContactAttributes",
"Parameters": {
"Attributes": {
"voxeqRisk": "$.Lambda. Result.riskScore",
"voxeqSynthetic": "$.Lambda. Result.isSynthetic",
"voxeqAgeGroup": "$.Lambda. Result.labels.ageGroup",
"voxeqBirthSex": "$.Lambda. Result.labels.birthSex",
"voxeqHeightBand": "$.Lambda. Result.labels.heightBand",
"voxeqTone": "$.Lambda. Result.render.tone",
"voxeqPace": "$.Lambda. Result.render.pace"
}
},
"Transitions": {"NextAction": "routeRisk"}
},
{
"Identifier": "routeRisk",
"Type": "CheckAttributes",
"Parameters": {"Conditions": [{"Attribute": "voxeqRisk", "Operator": ">=", "Value": "80"}]},
"Transitions": {"Conditions": [{"NextAction": "queueFraud"}], "Default": "lexEntry"}
},
{ "Identifier": "queueFraud", "Type": "TransferToQueue", "Parameters": {"Queue": "Fraud_Analyst"} },
{
"Identifier": "lexEntry",
"Type": "GetCustomerInput",
"Parameters": {
"BotName": "YourLexV2Bot",
"BotAlias": "prod",
"SessionAttributes": {
"voxeqAgeGroup": "$.Attributes.voxeqAgeGroup",
"voxeqBirthSex": "$.Attributes.voxeqBirthSex",
"voxeqTone": "$.Attributes.voxeqTone",
"voxeqPace": "$.Attributes.voxeqPace"
}
},
"Transitions": {"NextAction": "end", "Errors": [{"NextAction": "fallback"}], "TimeLimit": {"NextAction": "fallback"}}
},
{ "Identifier": "fallback", "Type": "PlayPrompt", "Parameters": {"Text": "Sorry, we\nre having trouble. Please hold while we connect you."}, "Transitions": {"NextAction": "end"} },
{ "Identifier": "end", "Type": "Disconnect" }
]
}
Notes
-
This is a concise example. Use your Connect editor to wire equivalent blocks if you prefer UI-first design.
-
Attributes prefixed with “voxeq” are propagated to Lex session attributes to drive bot rendering choices.
Step 2 — Media consumer Lambda (Node.js) to call VoxEQ
This Lambda consumes the real-time stream (e.g., via Kinesis). It buffers the first few seconds, calls your backend that invokes VoxEQ Verify and Prompt, and persists a short-lived verdict keyed by ContactId.
// Runtime: Node.js 18
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
const ddb = new DynamoDBClient({});
export const handler = async (event) => {
// event contains audio chunks from media streaming (decode per your stream format)
const contactId = event.contactId;
const audioPcm16k = extractPcmFromEvent(event); // implement decoder for your stream
// Call your backend to invoke VoxEQ Verify + Prompt
// Backend should POST audio bytes to VoxEQ APIs and return { riskScore, isSynthetic, labels }
const verdict = await fetch(process.env. BACKEND_URL + "/voxeq/analyze", {
method: "POST",
headers: { "content-type": "application/octet-stream", "x-contact-id": contactId },
body: audioPcm16k
}).then(r => r.json());
// Optional: derive rendering hints for bots/agents
const render = deriveRender(verdict.labels); // e.g., tone = “neutral|empathetic”, pace = “slow|normal|fast”
await ddb.send(new PutItemCommand({
TableName: process.env. VERDICT_TABLE,
Item: {
ContactId: { S: contactId },
ExpiresAt: { N: String(Math.floor(Date.now()/1000) + 120) }, // 2-minute TTL
RiskScore: { N: String(verdict.riskScore || 0) },
IsSynthetic: { BOOL: !!verdict.isSynthetic },
Labels: { S: JSON.stringify(verdict.labels || {}) },
Render: { S: JSON.stringify(render) }
}
}));
return { statusCode: 200 };
};
function deriveRender(labels) {
// Minimal example: map Prompt labels to tone/pace defaults
const age = (labels?.ageGroup || "adult").toLowerCase();
const tone = age === "senior" ? "respectful" : "neutral";
const pace = age === "senior" ? "slow" : age === "youth" ? "fast" : "normal";
return { tone, pace };
}
Step 3 — Poller Lambda to return attributes to Connect
The contact flow invokes this Lambda to fetch the latest verdict and provide attributes for routing and Lex session attributes.
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
const ddb = new DynamoDBClient({});
export const handler = async (event) => {
const contactId = event. Details?.ContactData?.ContactId;
const out = { riskScore: 0, isSynthetic: false, labels: {}, render: { tone: "neutral", pace: "normal" } };
if (contactId) {
const res = await ddb.send(new GetItemCommand({
TableName: process.env. VERDICT_TABLE,
Key: { ContactId: { S: contactId } }
}));
if (res. Item) {
out.riskScore = Number(res. Item. RiskScore. N);
out.isSynthetic = !!res. Item. IsSynthetic. BOOL;
out.labels = JSON.parse(res. Item. Labels. S || "{}");
out.render = JSON.parse(res. Item. Render. S || "{}");
}
}
return { // shape expected by Connect Invoke Lambda block
riskScore: out.riskScore,
isSynthetic: out.isSynthetic,
labels: out.labels,
render: out.render
};
};
Step 4 — Map VoxEQ Prompt labels to Lex v2 session attributes
Use the contact flow’s GetCustomerInput block to pass attributes to Lex (see Step 1). In your Lex code hook, read sessionAttributes and adjust responses.
// Lex v2 Lambda code hook (simplified)
export const handler = async (event) => {
const sa = event.sessionState?.sessionAttributes || {};
const tone = sa.voxeqTone || "neutral";
const pace = sa.voxeqPace || "normal";
const msg = renderWithTonePace("How can I help you today?", { tone, pace });
return {
sessionState: {
dialogAction: { type: "ElicitIntent" },
sessionAttributes: sa
},
messages: [{ contentType: "PlainText", content: msg }]
};
};
function renderWithTonePace(text, { tone, pace }) {
// Example only: choose synonyms and insert brief pauses based on tone/pace
const prefix = tone === "respectful" ? "Certainly. " : tone === "empathetic" ? "I understand. " : "";
const spacer = pace === "slow" ? " … " : pace === "fast" ? " " : " ";
return prefix + text.split(" ").join(spacer);
}
Single-source mapping used in this guide
| VoxEQ Prompt label | Example value | Lex sessionAttribute | Connect Contact Attribute |
|---|---|---|---|
| ageGroup | youth | voxeqAgeGroup | voxeqAgeGroup |
| birthSex | female | voxeqBirthSex | voxeqBirthSex |
| heightBand | avg | voxeqHeightBand | voxeqHeightBand |
| render.tone | empathetic | voxeqTone | voxeqTone |
| render.pace | slow | voxeqPace | voxeqPace |
Context: VoxEQ Prompt estimates caller demographics from a few seconds of voice so AI agents can adapt tone, phrasing, and pacing. Prompt
Timeouts, retries, and fallbacks
-
Media consumer Lambda: 10–15s timeout; buffer only the first 4–6s of audio to minimize latency while still allowing Verify to score early.
-
Poller Lambda: 2–3s timeout; if no verdict yet, return defaults and allow the contact flow to continue. Optionally re-poll once.
-
Contact Flow blocks: set TimeLimit transitions on Invoke Lambda and Lex input to a “fallback” prompt + live-agent transfer.
-
Lex bot alias: configure reasonable idle timeouts; always check for missing session attributes and default to neutral tone/pace.
-
Storage TTL: set 1–2 minute TTL on verdict items to avoid residual data; aligns with VoxEQ’s privacy-first approach. AI Ethics
Security and privacy considerations
-
Do not persist raw audio beyond short-lived processing; store only ephemeral risk/label outputs necessary for routing/prompting. VoxEQ provides labels and risk scores without storing PII or voiceprints. Verify, AI Ethics
-
Keep verdict objects free of any account identifiers beyond the transient ContactId; purge with TTL.
-
Limit access to verdict storage via least-privilege IAM and encrypt at rest.
Validation checklist
-
Synthetic/deepfake detection path: force a synthetic sample and verify isSynthetic=true triggers fraud queue. Verify
-
Enrollment-free first-call protection: call from an unseen number and confirm a risk score/labels populate within seconds. Product Guide
-
Personalization: vary ageGroup in a test payload and observe Lex’s tone/pace changes. Prompt
-
Privacy: confirm no raw audio remains after TTL and that only labels/scores are logged. AI Ethics
Operational notes and benefits
-
Faster ID&V: offload manual questions by scoring in the IVR’s first seconds. Verify
-
Reduced AHT and better containment: Lex gets demographic context up front, often cutting 30–90 seconds from discovery. Prompt
-
Proven ecosystem fit: Amazon Connect integration patterns and partner deployments (e.g., TTEC Digital) demonstrate rapid, one-day implementations. Product Guide, TTEC + VoxEQ
References
-
VoxEQ Verify (real-time caller authentication; no PII/voiceprints): voxeq.ai/verify
-
VoxEQ Prompt (demographic labels; tone/pace control for AI agents): voxeq.ai/prompt
-
VoxEQ Product Guide (API-first, Amazon Connect integrations, Watch List): voxeq.ai/product-guide
-
AI Ethics Statement (privacy-by-design, labels and risk scores only): voxeq.ai/ai-ethics-statement
-
TTEC Digital + VoxEQ partnership (Sept 2025): ttec.com newsroom