Teleperson
Integrations

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:

  1. Storage — your credentials live on a row in vapi_configs keyed by your company id. There is exactly one row per company.
  2. Reads — Analytics queries filter vapi_call_events.company_id to the authenticated caller's company. Row-Level Security enforces this even if application code forgets.
  3. Webhooks — incoming events are routed to the owning tenant by matching assistant.id against the assistant_ids array. Events whose assistantId isn't registered to any tenant are dropped.
  4. REST pulls — server-side backfills filter every /call request to assistantId ∈ {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:

FieldWhere to find itRequired?
Assistant ID(s)Assistants → your assistant → IDYes
Public KeyOrg Settings → API Keys → PublicYes
Private KeyOrg Settings → API Keys → PrivateYes
Webhook secretOrg Settings → Server URL secretOptional

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

  1. Sign in to Teleperson as a company admin or owner.
  2. Click your avatar in the top-right → Integrations.
  3. On the Vapi card, click Connect.
  4. Paste your Assistant ID(s) (one per line), Public Key, and Private Key.
  5. (Optional) Paste a Webhook secret if you want the platform to verify incoming webhook signatures.
  6. 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-webhook

Use 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 the store_transcripts flag on your integration.

On this page