Vapi (Voice AI)
Connect your Vapi voice assistant so Teleperson Analytics tracks your calls — scoped strictly to your company and your Assistant IDs.
Vapi (Voice AI) integration
Teleperson can ingest call data from Vapi so your team sees voice activity inside Analytics — call volume, total minutes, average duration, ended-reason breakdown, and cost — alongside the rest of your customer activity.
How scoping works
Every Vapi integration is bound to one Teleperson company and to the
specific Vapi Assistant ID(s) you list. If you're DCA and you configure
Assistant asst_abc123, Analytics only shows calls handled by asst_abc123.
Another customer configuring their own Assistant ID never sees your traffic,
and you never see theirs.
Concretely, this is enforced in four places:
- Storage — your credentials live on a row in
vapi_configskeyed by your company id. There is exactly one row per company. - Reads — Analytics queries filter
vapi_call_events.company_idto the authenticated caller's company. Row-Level Security enforces this even if application code forgets. - Webhooks — incoming events are routed to the owning tenant by matching
assistant.idagainst theassistant_idsarray. Events whose assistantId isn't registered to any tenant are dropped. - REST pulls — server-side backfills filter every
/callrequest toassistantId ∈ {your configured IDs}and stamp the resulting rows with your company id.
You can list multiple Assistant IDs under one integration (e.g. an inbound sales assistant + a support assistant) — Analytics aggregates them and also shows a per-assistant breakdown.
Only list the assistants you want tracked
If your team runs multiple Vapi assistants for different purposes, only list the ones whose calls you want surfaced in Analytics. A common pattern:
- ✅ Customer-facing assistant (e.g. an inbound support or sales bot) → list its Assistant ID here. Its calls flow into Analytics.
- ❌ Internal-only assistant (e.g. an executive Q&A agent embedded inside your own product for staff use) → leave its Assistant ID out. Its calls are dropped at the webhook and never stored.
This is true even when both assistants live under the same Vapi organization and share the same Public/Private Key. Routing is per-Assistant-ID, not per-org — so an internal assistant remains fully isolated from Analytics just by not appearing in the list above.
What you need from Vapi
From your Vapi dashboard, copy:
| Field | Where to find it | Required? |
|---|---|---|
| Assistant ID(s) | Assistants → your assistant → ID | Yes |
| Public Key | Org Settings → API Keys → Public | Yes |
| Private Key | Org Settings → API Keys → Private | Yes |
| Webhook secret | Org Settings → Server URL secret | Optional |
The Private Key unlocks transcripts, cost, ended-reason, and duration data — without it, Analytics would be limited to whatever the browser widget reports. Teleperson stores your Private Key AES-256-GCM encrypted at rest and never returns it to the browser; only server-side edge functions can decrypt it to call the Vapi REST API on your behalf.
Setup
- Sign in to Teleperson as a company admin or owner.
- Click your avatar in the top-right → Integrations.
- On the Vapi card, click Connect.
- Paste your Assistant ID(s) (one per line), Public Key, and Private Key.
- (Optional) Paste a Webhook secret if you want the platform to verify incoming webhook signatures.
- Click Save. You should see the Connected badge.
Real-time updates via webhook
To stream call data in real time rather than waiting for a manual sync, add this webhook URL in your Vapi server configuration:
https://<your-supabase-project>.functions.supabase.co/vapi-webhookUse the End-of-Call Report event. Teleperson matches every incoming event
to the owning tenant by assistant.id. Events whose assistantId is unknown
are discarded — they never reach your data or anyone else's.
If you set a Webhook secret in the integration card, the platform verifies an
X-Vapi-Signature HMAC-SHA256 header on every incoming event and rejects
anything that doesn't match.
One-shot backfill
Once your credentials are in, click Sync now on the Vapi card. Teleperson will pull up to the last 100 calls per assistant from Vapi's REST API and populate your Analytics. Subsequent calls flow in via the webhook.
What Analytics shows
Once configured and after the first call arrives, the Analytics page surfaces a Voice (Vapi) panel with:
- Total calls (selectable window — default 30 days)
- Total minutes handled
- Average call duration
- Total cost (USD)
- Top ended reason (e.g.
customer-ended-call,assistant-hangup) - By assistant (if you connected more than one)
All numbers are filtered server-side to your configured Assistant IDs and your company id. The panel auto-hides for any user whose company has not yet configured Vapi or who has no calls in the window.
Updating or removing credentials
- Rotate Public/Private key — reopen the Vapi card → Manage, paste the new values, and Save. Leaving Private Key blank keeps the existing encrypted value.
- Add or remove an Assistant ID — reopen the Vapi card → Manage, update the list, and Save. Calls for removed assistants stop flowing in immediately; historical events for them remain visible in Analytics until you ask support to purge.
- Fully disconnect — contact support to remove the row and purge stored call history. We currently surface key rotation in-product but require a support request for a destructive purge.
Security & data handling
- Private Key: AES-256-GCM encrypted with a server-side master key. Never returned to the browser. Only edge functions running with the service-role key can decrypt.
- Public Key: stored as-is (it's intended to be publicly visible per Vapi's design).
- Webhook secret: stored as-is, used for per-event HMAC verification inside the webhook function.
- Call records: stored under
vapi_call_events, RLS-scoped to the owning company. Cross-tenant reads are impossible at the database layer. - Transcripts and PII: if your Vapi calls include sensitive content,
transcripts are stored verbatim in
vapi_call_events.transcript. If you'd rather we drop transcripts on ingest, ask support to flip thestore_transcriptsflag on your integration.