Collecte et analyse de données avec la plateforme ElevenAgents dans Next.js

Collectez et analysez des données dans des webhooks post-appel avec la plateforme ElevenAgents et Next.js.

Tutoriel · Suppose que vous avez suivi le guide de démarrage rapide d’ElevenAgents et configuré un projet Next.js.

Introduction

Dans ce tutoriel, vous apprendrez à créer un agent vocal qui collecte des informations auprès de l’utilisateur au fil de la conversation, puis analyse et extrait les données de manière structurée avant de les envoyer à votre application via le webhook post-appel.

Prérequis

  • Un compte ElevenLabs avec une clé API.
  • Node.js v18 ou une version ultérieure installé sur votre machine.

Configuration

Créer un projet Next.js

Nous vous recommandons d’utiliser notre modèle de plateforme ElevenAgents v0.dev comme point de départ pour votre application. Ce modèle est une application Next.js prête pour la production, avec l’agent ElevenLabs déjà intégré.

Configurer la plateforme ElevenAgents

Suivez notre guide Next.js pour les étapes d’installation et de configuration. Revenez ensuite ici pour créer les fonctionnalités avancées.

Configuration de l’agent

1

Se connecter à ElevenLabs

Accédez à elevenlabs.io et connectez-vous à votre compte.

2

Créer un agent

Accédez à Agents Platform > Agents et créez un agent à partir du modèle vierge.

3

Définir le premier message

Définissez le premier message et indiquez la variable dynamique de la plateforme.

Hi {{user_name}}, I'm Jess from the ElevenLabs team. I'm here to help you design your very own ElevenLabs agent! To kick things off, let me know what kind of agent you're looking to create. For example, do you want a support agent, to help your users answer questions, or a sales agent to sell your products, or just a friend to chat with?
4

Définir le prompt système

Définissez le prompt système. Vous pouvez également y inclure des variables dynamiques.

You are Jess, a helpful agent helping {{user_name}} to design their very own ElevenLabs agent. The design process involves the following steps:
"initial": In the first step, collect the information about the kind of agent the user is looking to create. Summarize the user's needs back to them and ask if they are ready to continue to the next step. Only once they confirm proceed to the next step.
"training": Tell the user to create the agent's knowledge base by uploading documents, or submitting URLs to public websites with information that should be available to the agent. Wait patiently without talking to the user. Only when the user confirms that they've provided everything then proceed to the next step.
"voice": Tell the user to describe the voice they want their agent to have. For example: "A professional, strong spoken female voice with a slight British accent." Repeat the description of their voice back to them and ask if they are ready to continue to the next step. Only once they confirm proceed to the next step.
"email": Tell the user that we've collected all necessary information to create their ElevenLabs agent and ask them to provide their email address to get notified when the agent is ready.
Always call the `set_ui_state` tool when moving between steps!
5

Configurer les outils client

Configurez l’outil client suivant pour naviguer entre les étapes :

  • Nom : set_ui_state
    • Description : Utilisez cet outil côté client pour naviguer entre les différents états de l’interface utilisateur.
    • Attendre la réponse : true
    • Délai d’expiration de la réponse (secondes) : 1
    • Paramètres :
      • Type de données : string
      • Identifiant : step
      • Obligatoire : true
      • Type de valeur : LLM Prompt
      • Description : Étape vers laquelle naviguer dans l’interface utilisateur. Utilisez uniquement les étapes définies dans le prompt système.
6

Définir la voix de votre agent

Accédez à l’onglet Voice et définissez la voix de votre agent. Vous trouverez une liste de voix recommandées pour la plateforme ElevenAgents dans la documentation de conception de voix conversationnelles.

7

Définir les critères d’évaluation

Accédez à l’onglet Analysis et ajoutez un critère d’évaluation.

  • Nom : all_data_provided
    • Prompt : Évaluez si l’utilisateur a fourni une description de l’agent qu’il souhaite générer, ainsi qu’une description de la voix que cet agent doit avoir.
8

Configurer la collecte de données

Vous pouvez utiliser l’analyse post-appel pour extraire des données de la conversation. Dans l’onglet Analysis, sous Data Collection, ajoutez les éléments suivants :

  • Identifiant : voice_description
    • data-type : String
    • Description : À partir de la description de la voix souhaitée par l’utilisateur pour l’agent, générez une description concise de la voix incluant l’âge, l’accent, le ton et le caractère, si ces informations sont disponibles.
  • Identifiant : agent_description
    • data-type : String
    • Description : À partir de la description de l’agent que l’utilisateur souhaite concevoir, générez un prompt pouvant servir à entraîner un modèle à agir comme cet agent.
9

Configurer le webhook post-appel

Les webhooks post-appel vous avertissent lorsqu’un appel se termine et que les étapes d’analyse et d’extraction de données sont terminées.

Dans cet exemple, le webhook post-appel effectue notamment les étapes suivantes :

  1. Créer une conception de voix personnalisée à partir de voice_description.
  2. Créer un agent ElevenLabs pour les utilisateurs à partir de la valeur agent_description qu’ils ont fournie.
  3. Récupérer les documents de la base de connaissances depuis l’état de la conversation stocké dans Redis et associer la base de connaissances à l’agent.
  4. Envoyer un email à l’utilisateur pour l’informer que son agent ElevenLabs personnalisé est prêt à converser.

Lors d’une exécution locale, vous aurez besoin d’un outil tel que ngrok pour exposer votre serveur local à Internet.

ngrok http 3000

Accédez aux paramètres de la plateforme ElevenAgents, puis sous Post-Call Webhook, créez un webhook et collez votre URL ngrok : https://<your-url>.ngrok-free.app/api/convai-webhook.

Après avoir enregistré le webhook, vous recevrez un secret de webhook. Conservez-le de manière sécurisée, car vous devrez ensuite le définir dans votre fichier .env.

Intégrer les fonctionnalités avancées

Configurer une base de données Redis pour stocker l’état de la conversation

Dans cet exemple, nous utilisons Redis pour stocker l’état de la conversation. Cela nous permet de récupérer les documents de la base de connaissances à partir de l’état de la conversation une fois l’appel terminé.

Si vous déployez sur Vercel, vous pouvez configurer l’intégration Upstash for Redis. Vous pouvez aussi créer un compte Upstash gratuit et une base de données.

Configurer Resend pour l’envoi des emails post-appel

Dans cet exemple, nous utilisons Resend pour envoyer l’email post-appel à l’utilisateur. Pour cela, créez un compte Resend gratuit et configurez une clé API.

Définir les variables d’environnement

À la racine de votre projet, créez un fichier .env et ajoutez les variables suivantes :

ELEVENLABS_CONVAI_WEBHOOK_SECRET=
ELEVENLABS_API_KEY=
ELEVENLABS_AGENT_ID=
# Resend
RESEND_API_KEY=
RESEND_FROM_EMAIL=
# Upstash Redis
KV_URL=
KV_REST_API_READ_ONLY_TOKEN=
REDIS_URL=
KV_REST_API_TOKEN=
KV_REST_API_URL=

Configurer la sécurité et l’authentification

Pour sécuriser votre agent ElevenLabs, vous devez activer l’authentification dans l’onglet Security de la configuration de l’agent.

Une fois l’authentification activée, vous devrez créer une URL signée dans un environnement serveur sécurisé afin de lancer une conversation avec l’agent. Dans Next.js, vous pouvez le faire en configurant une nouvelle route API.

./app/api/signed-url/route.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { NextResponse } from "next/server";
export async function GET() {
const agentId = process.env.ELEVENLABS_AGENT_ID;
if (!agentId) {
throw Error("ELEVENLABS_AGENT_ID is not set");
}
try {
const elevenlabs = new ElevenLabsClient();
const response = await elevenlabs.conversationalAi.conversations.getSignedUrl({
agentId,
});
return NextResponse.json({ signedUrl: response.signedUrl });
} catch (error) {
console.error("Error:", error);
return NextResponse.json({ error: "Failed to get signed URL" }, { status: 500 });
}
}

Démarrer la session de conversation

Pour démarrer la conversation, appelez d’abord votre route API afin d’obtenir l’URL signée, puis utilisez le hook useConversation pour configurer la session de conversation.

./page.tsx
import { useConversation } from "@elevenlabs/react";
async function getSignedUrl(): Promise<string> {
const response = await fetch("/api/signed-url");
if (!response.ok) {
throw Error("Failed to get signed url");
}
const data = await response.json();
return data.signedUrl;
}
export default function Home() {
// ...
const [currentStep, setCurrentStep] = useState<
"initial" | "training" | "voice" | "email" | "ready"
>("initial");
const [conversationId, setConversationId] = useState("");
const [userName, setUserName] = useState("");
const conversation = useConversation({
onConnect: () => console.log("Connected"),
onDisconnect: () => console.log("Disconnected"),
onMessage: (message: string) => console.log("Message:", message),
onError: (error: Error) => console.error("Error:", error),
});
const startConversation = useCallback(async () => {
try {
// Request microphone permission
await navigator.mediaDevices.getUserMedia({ audio: true });
// Start the conversation with your agent
const signedUrl = await getSignedUrl();
const convId = await conversation.startSession({
signedUrl,
dynamicVariables: {
user_name: userName,
},
clientTools: {
set_ui_state: ({ step }: { step: string }): string => {
// Allow agent to navigate the UI.
setCurrentStep(step as "initial" | "training" | "voice" | "email" | "ready");
return `Navigated to ${step}`;
},
},
});
setConversationId(convId);
console.log("Conversation ID:", convId);
} catch (error) {
console.error("Failed to start conversation:", error);
}
}, [conversation, userName]);
const stopConversation = useCallback(async () => {
await conversation.endSession();
}, [conversation]);
// ...
}

Outil client et variables dynamiques

Lors de la configuration de l’agent, vous avez enregistré l’outil client set_ui_state afin de permettre à l’agent de naviguer entre les différents états de l’interface utilisateur. Pour tout relier, vous devez transmettre l’implémentation de l’outil client aux options de conversation.startSession.

C’est également ici que vous pouvez transmettre les variables dynamiques à la conversation.

./page.tsx
const convId = await conversation.startSession({
signedUrl,
dynamicVariables: {
user_name: userName,
},
clientTools: {
set_ui_state: ({ step }: { step: string }): string => {
// Allow agent to navigate the UI.
setCurrentStep(step as "initial" | "training" | "voice" | "email" | "ready");
return `Navigated to ${step}`;
},
},
});

Importer des documents dans la base de connaissances

À l’étape Training, l’agent demandera à l’utilisateur d’importer des documents ou de soumettre des URL de sites web publics contenant des informations qui devront être accessibles à son agent. Vous pouvez ici tirer parti de la nouvelle fonction after de Next.js 15 pour autoriser l’importation de documents en arrière-plan.

Créez une nouvelle action serveur upload afin de gérer la création de la base de connaissances lors de la soumission du formulaire. Une fois tous les documents de la base de connaissances créés, stockez l’ID de conversation et les ID de la base de connaissances dans la base de données Redis.

./app/actions/upload.ts
"use server";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { Redis } from "@upstash/redis";
import { redirect } from "next/navigation";
import { after } from "next/server";
// Initialize Redis
const redis = Redis.fromEnv();
const elevenlabs = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY,
});
export async function uploadFormData(formData: FormData) {
const knowledgeBase: Array<{
id: string;
type: "file" | "url";
name: string;
}> = [];
const files = formData.getAll("file-upload") as File[];
const email = formData.get("email-input");
const urls = formData.getAll("url-input");
const conversationId = formData.get("conversation-id");
after(async () => {
// Upload files as background job
// Create knowledge base entries
// Loop through files and create knowledge base entries
for (const file of files) {
if (file.size > 0) {
const response = await elevenlabs.conversationalAi.knowledgeBase.documents.createFromFile({
file,
});
if (response.id) {
knowledgeBase.push({
id: response.id,
type: "file",
name: file.name,
});
}
}
}
// Append all urls
for (const url of urls) {
const response = await elevenlabs.conversationalAi.knowledgeBase.documents.createFromUrl({
url: url as string,
});
if (response.id) {
knowledgeBase.push({
id: response.id,
type: "url",
name: `url for ${conversationId}`,
});
}
}
// Store knowledge base IDs and conversation ID in database.
const redisRes = await redis.set(
conversationId as string,
JSON.stringify({ email, knowledgeBase })
);
console.log({ redisRes });
});
redirect("/success");
}

Gérer le webhook post-appel

Le webhook post-appel est déclenché lorsqu’un appel se termine et que les étapes d’analyse et d’extraction de données sont terminées.

Voici les différentes étapes effectuées :

  1. Vérifier le secret du webhook et construire la charge utile du webhook.
  2. Créer une conception de voix personnalisée à partir de voice_description.
  3. Créer un agent ElevenLabs pour les utilisateurs à partir de la valeur agent_description qu’ils ont fournie.
  4. Récupérer les documents de la base de connaissances depuis l’état de la conversation stocké dans Redis et associer la base de connaissances à l’agent.
  5. Envoyer un email à l’utilisateur pour l’informer que son agent ElevenLabs personnalisé est prêt à converser.
./app/api/convai-webhook/route.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { Redis } from "@upstash/redis";
import crypto from "crypto";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { Resend } from "resend";
import { EmailTemplate } from "@/components/email/post-call-webhook-email";
// Initialize Redis
const redis = Redis.fromEnv();
// Initialize Resend
const resend = new Resend(process.env.RESEND_API_KEY);
const elevenlabs = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY,
});
export async function GET() {
return NextResponse.json({ status: "webhook listening" }, { status: 200 });
}
export async function POST(req: NextRequest) {
const secret = process.env.ELEVENLABS_CONVAI_WEBHOOK_SECRET; // Add this to your env variables
const { event, error } = await constructWebhookEvent(req, secret);
if (error) {
return NextResponse.json({ error: error }, { status: 401 });
}
if (event.type === "post_call_transcription") {
const { conversation_id, analysis, agent_id } = event.data;
if (
agent_id === process.env.ELEVENLABS_AGENT_ID &&
analysis.evaluation_criteria_results.all_data_provided?.result === "success" &&
analysis.data_collection_results.voice_description?.value
) {
try {
// Design the voice
const voicePreview = await elevenlabs.textToVoice.createPreviews({
voiceDescription: analysis.data_collection_results.voice_description.value,
text: "The night air carried whispers of betrayal, thick as London fog. I adjusted my cufflinks - after all, even spies must maintain appearances, especially when the game is afoot.",
});
const voice = await elevenlabs.textToVoice.createVoiceFromPreview({
voiceName: `voice-${conversation_id}`,
voiceDescription: `Voice for ${conversation_id}`,
generatedVoiceId: voicePreview.previews[0].generatedVoiceId,
});
// Get the knowledge base from redis
const redisRes = await getRedisDataWithRetry(conversation_id);
if (!redisRes) throw new Error("Conversation data not found!");
// Handle agent creation
const agent = await elevenlabs.conversationalAi.agents.create({
name: `Agent for ${conversation_id}`,
conversationConfig: {
tts: { voiceId: voice.voiceId },
agent: {
prompt: {
prompt:
analysis.data_collection_results.agent_description?.value ??
"You are a helpful assistant.",
knowledgeBase: redisRes.knowledgeBase,
},
firstMessage: "Hello, how can I help you today?",
},
},
});
console.log("Agent created", { agent: agent.agentId });
// Send email to user
console.log("Sending email to", redisRes.email);
await resend.emails.send({
from: process.env.RESEND_FROM_EMAIL!,
to: redisRes.email,
subject: "Your ElevenLabs agent is ready to chat!",
react: EmailTemplate({ agentId: agent.agentId }),
});
} catch (error) {
console.error(error);
return NextResponse.json({ error }, { status: 500 });
}
}
}
return NextResponse.json({ received: true }, { status: 200 });
}
const constructWebhookEvent = async (req: NextRequest, secret?: string) => {
const body = await req.text();
const signatureHeader = req.headers.get("ElevenLabs-Signature");
return await elevenlabs.webhooks.constructEvent(body, signatureHeader, secret);
};
async function getRedisDataWithRetry(
conversationId: string,
maxRetries = 5
): Promise<{
email: string;
knowledgeBase: Array<{
id: string;
type: "file" | "url";
name: string;
}>;
} | null> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const data = await redis.get(conversationId);
return data as any;
} catch (error) {
if (attempt === maxRetries) throw error;
console.log(`Redis get attempt ${attempt} failed, retrying...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return null;
}

Examinons chaque étape en détail.

Vérifier le secret du webhook et construire la charge utile du webhook

Lorsque la requête webhook est reçue, nous vérifions d’abord le secret du webhook et construisons la charge utile du webhook.

./app/api/convai-webhook/route.ts
// ...
export async function POST(req: NextRequest) {
const secret = process.env.ELEVENLABS_CONVAI_WEBHOOK_SECRET;
const { event, error } = await constructWebhookEvent(req, secret);
// ...
}
// ...
const constructWebhookEvent = async (req: NextRequest, secret?: string) => {
const body = await req.text();
const signatureHeader = req.headers.get("ElevenLabs-Signature");
return await elevenlabs.webhooks.constructEvent(body, signatureHeader, secret);
};
async function getRedisDataWithRetry(
conversationId: string,
maxRetries = 5
): Promise<{
email: string;
knowledgeBase: Array<{
id: string;
type: "file" | "url";
name: string;
}>;
} | null> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const data = await redis.get(conversationId);
return data as any;
} catch (error) {
if (attempt === maxRetries) throw error;
console.log(`Redis get attempt ${attempt} failed, retrying...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return null;
}

Créer une conception de voix personnalisée à partir de voice_description

À l’aide de la valeur voice_description de la charge utile du webhook, nous créons une conception de voix personnalisée.

./app/api/convai-webhook/route.ts
// ...
// Design the voice
const voicePreview = await elevenlabs.textToVoice.createPreviews({
voiceDescription: analysis.data_collection_results.voice_description.value,
text: "The night air carried whispers of betrayal, thick as London fog. I adjusted my cufflinks - after all, even spies must maintain appearances, especially when the game is afoot.",
});
const voice = await elevenlabs.textToVoice.createVoiceFromPreview({
voiceName: `voice-${conversation_id}`,
voiceDescription: `Voice for ${conversation_id}`,
generatedVoiceId: voicePreview.previews[0].generatedVoiceId,
});
// ...

Récupérer les documents de la base de connaissances depuis l’état de la conversation stocké dans Redis

L’importation des documents peut prendre plus de temps que l’analyse des données du webhook. Nous devons donc interroger l’état de la conversation dans Redis jusqu’à ce que les documents soient importés.

./app/api/convai-webhook/route.ts
// ...
// Get the knowledge base from redis
const redisRes = await getRedisDataWithRetry(conversation_id);
if (!redisRes) throw new Error("Conversation data not found!");
// ...
async function getRedisDataWithRetry(
conversationId: string,
maxRetries = 5
): Promise<{
email: string;
knowledgeBase: Array<{
id: string;
type: "file" | "url";
name: string;
}>;
} | null> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const data = await redis.get(conversationId);
return data as any;
} catch (error) {
if (attempt === maxRetries) throw error;
console.log(`Redis get attempt ${attempt} failed, retrying...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return null;
}

Créer un agent ElevenLabs pour les utilisateurs à partir de la valeur agent_description qu’ils ont fournie

Créez l’agent ElevenLabs pour l’utilisateur à partir de la valeur agent_description qu’il a fournie, puis associez à l’agent la conception de voix et la base de connaissances nouvellement créées.

./app/api/convai-webhook/route.ts
// ...
// Handle agent creation
const agent = await elevenlabs.conversationalAi.agents.create({
name: `Agent for ${conversationId}`,
conversationConfig: {
tts: { voiceId: voice.voiceId },
agent: {
prompt: {
prompt:
analysis.data_collection_results.agent_description?.value ??
"You are a helpful assistant.",
knowledgeBase: redisRes.knowledgeBase,
},
firstMessage: "Hello, how can I help you today?",
},
},
});
console.log("Agent created", { agent: agent.agentId });
// ...

Envoyer un email à l’utilisateur pour l’informer que son agent ElevenLabs personnalisé est prêt à converser

Une fois l’agent créé, vous pouvez envoyer un email à l’utilisateur pour l’informer que son agent ElevenLabs personnalisé est prêt à converser.

./app/api/convai-webhook/route.ts
import { Resend } from "resend";
import { EmailTemplate } from "@/components/email/post-call-webhook-email";
// ...
// Send email to user
console.log("Sending email to", redisRes.email);
await resend.emails.send({
from: process.env.RESEND_FROM_EMAIL!,
to: redisRes.email,
subject: "Your ElevenLabs agent is ready to chat!",
react: EmailTemplate({ agentId: agent.agentId }),
});
// ...

Vous pouvez utiliser new.email, un outil pratique de l’équipe Resend, pour concevoir vos modèles d’email de façon intuitive. Lorsque le modèle vous convient, créez un composant et ajoutez l’ID de l’agent en tant que prop.

./components/email/post-call-webhook-email.tsx
import {
Body,
Button,
Container,
Head,
Html,
Section,
Text,
Tailwind,
} from "@react-email/components";
import * as React from "react";
const EmailTemplate = (props: any) => {
const { agentId } = props;
return (
<Html>
<Head />
<Tailwind>
<Body className="bg-[#151516] font-sans">
<Container className="mx-auto my-[40px] max-w-[600px] rounded-[8px] bg-[#0a1929] p-[20px]">
{/* Top Section */}
<Section className="mb-[32px] mt-[32px] text-center">
<Text className="m-0 text-[28px] font-bold text-[#9c27b0]">
Your ElevenLabs agent is ready to chat!
</Text>
</Section>
{/* Content Area with Icon */}
<Section className="mb-[32px] text-center">
{/* Circle Icon with Checkmark */}
<div className="mx-auto mb-[24px] flex h-[80px] w-[80px] items-center justify-center rounded-full bg-gradient-to-r from-[#9c27b0] to-[#3f51b5]">
<div className="text-[40px] text-white">✓</div>
</div>
{/* Descriptive Text */}
<Text className="mb-[24px] text-[18px] text-white">
Your ElevenLabs agent is ready to chat!
</Text>
</Section>
{/* Call to Action Button */}
<Section className="mb-[32px] text-center">
<Button
href={`https://elevenlabs.io/app/talk-to?agent_id=${agentId}`}
className="box-border rounded-[8px] bg-[#9c27b0] px-[40px] py-[20px] text-[24px] font-bold text-white no-underline"
>
Chat now!
</Button>
</Section>
{/* Footer */}
<Section className="mt-[40px] border-t border-[#2d3748] pt-[20px] text-center">
<Text className="m-0 text-[14px] text-white">
Powered by{" "}
<a
href="https://elevenlabs.io/conversational-ai"
target="_blank"
rel="noopener noreferrer"
className="underline transition-colors hover:text-gray-400"
>
ElevenLabs Agents
</a>
</Text>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
);
};
export { EmailTemplate };

Exécuter l’application

Pour exécuter l’application localement de bout en bout, démarrez d’abord le serveur de développement Next.js, puis exécutez le tunnel ngrok dans un terminal distinct afin d’exposer le gestionnaire de webhook à Internet.

  • Terminal 1 :
    • Exécutez pnpm dev pour démarrer le serveur de développement Next.js.
pnpm dev
  • Terminal 2 :
    • Exécutez ngrok http 3000 pour exposer le gestionnaire de webhook à Internet.
ngrok http 3000

Ouvrez maintenant http://localhost:3000 et commencez à concevoir votre agent ElevenLabs personnalisé, avec votre propre voix.

Prochaines étapes