A call, end to end
This page traces a single inbound call from the moment it rings to the moment it ends — who talks to whom, and exactly which Confirm Reseller API call you make at each point. Everything you are responsible for is the Reseller lane; the Pindrop ↔ Confirm exchanges are shown so the timing makes sense, but they happen for you.
Who's involved
| Participant | Role |
|---|---|
| Caller | The member phoning the contact centre. |
| Phone system SIPREC / SBC | Your (or the institution's) telephony. It forks a copy of the call audio to Pindrop over SIPREC and carries the unique SIP header that ties the leg to a Pindrop call. |
| Pindrop | Voice biometrics, device / carrier risk and liveness analysis. Issues the call's voiceId and pushes events to Confirm. |
| Confirm | Holds the member record, the call record and the authentication policy. The only system you call. |
| Reseller you | Your system — an agent desktop, a conversational AI or IVR platform, or middleware. Calls /external/* to claim the call, read results, and accept or reject the voice. |
Account correlation, authentication-result retrieval and authentication feedback are all made by Confirm on your behalf. Your integration is a handful of HTTP calls: queue, assign, details, and — only when it's needed — enroll or reject-voice.
Getting the voiceId
Every call-scoped endpoint is keyed on the call's voiceId, so the first question in any integration is how you get one. There are three ways, and most integrations use the first or the second.
1. Read it from the call queue
GET /external/calls/queue returns the institution's active calls — ones that haven't ended — each with its voiceId, the caller's phoneNumber, the start time, and the candidate members Confirm already matched from the core on that phone number. That last part is why this is the usual starting point: it gives you both the call handle and a shortlist of personId values to claim it with.
{
"success": true,
"phoneCalls": [
{
"voiceId": "b1f0…9c2e",
"phoneNumber": "+16165551212",
"personId": null,
"startDateTime": "2026-06-14T21:58:11+00:00",
"persons": [ { "name": "Jane Doe", … } ]
}
]
}
Supports searchString (matches the phone number or a matched member's name), startDate / endDate, and continueFrom / pageSize paging. Numbers on the institution's ignore list are filtered out.
2. Let Confirm resolve it from the phone number
If you already know the caller's number and which member is on the line, skip the lookup: POST /external/calls/assign with phoneNumber + personId, and the response hands you the voiceId to use from then on.
3. Receive a push notification from Confirm
Confirm can POST call events to an endpoint you own. An institution registers the URL under Settings → Push Notifications in the Confirm dashboard, optionally with custom request headers (entered as Header: value pairs separated by ; — use these to carry your own auth). Two event types are sent:
| Type | Fired when |
|---|---|
CallStart | Pindrop reports the call has started — this is the earliest you can know the voiceId. |
CallEnd | Pindrop reports the call has ended. |
{
"Type": "CallStart",
"StartDateTime": "2026-06-14T21:58:11.4821Z",
"VoiceId": "b1f0…9c2e",
"AlternateVoiceId": "k3Jd…9Aw",
"PhoneNumber": "+16165551212",
"DestinationPhoneNumber": "+18005550100",
"IsTest": null,
"InstitutionKey": "acme-cu"
}
CallEnd is the same shape with "Type": "CallEnd" and EndDateTime in place of StartDateTime. Note the PascalCase property names — these payloads are serialized differently from the API's own JSON responses.
Confirm POSTs once and does not retry. Every attempt is recorded with the response status and body, visible on the same Push Notifications settings page, and that page can fire a sample CallStart / CallEnd at your endpoint while you build (samples carry "IsTest": true; real events don't). Treat the push as a low-latency nudge and keep the queue as your source of truth.
AlternateVoiceId works anywhere a voiceId does
Pindrop issues a second identifier for a call, delivered as AlternateVoiceId. assign, …/details and …/enroll all resolve an alternate id back to the real call, so you can pass whichever one you're holding.
The sequence diagram
sequenceDiagram autonumber actor Caller participant PBX as Phone system
SIPREC / SBC participant PD as Pindrop participant CF as Confirm participant RS as Reseller Caller->>PBX: Call rings PBX-->>PD: Forked / forwarded audio Note over PD,CF: When the call is answered PD->>CF: Push notification — unique Call ID (voiceId) CF--)RS: Call-start push notification (if you registered an endpoint) rect rgb(230, 240, 251) Note over RS,CF: 1. Find the call and claim it RS->>CF: GET /external/calls/queue CF-->>RS: HTTP 200 — active calls, each with its voiceId RS->>CF: POST /external/calls/assign
voiceId or phoneNumber, + personId CF->>PD: Account correlation PD-->>CF: HTTP 200 CF-->>RS: HTTP 200 — { success, voiceId } end rect rgb(226, 245, 236) Note over RS,CF: 2. Read authentication results (poll) RS->>CF: GET /external/calls/{voiceId}/details CF->>PD: Fetch authentication results PD-->>CF: Authentication results CF-->>RS: HTTP 200 — policies, flags, Pindrop events end rect rgb(253, 244, 216) Note over RS,CF: 3. Decide — your system verified the caller RS->>CF: POST /external/calls/{voiceId}/enroll
personId + validationType CF->>PD: Account authentication feedback — SUCCESSFUL PD-->>CF: HTTP 200 CF-->>RS: HTTP 200 — { success } end Note over RS,CF: Or POST …/reject-voice → FAILED feedback, voiceprint un-enrolled rect rgb(238, 241, 244) Note over PD,CF: Call ended PD->>CF: Call-ended event — latest call analysis CF-->>PD: HTTP 200 CF--)RS: Call-end push notification (if you registered an endpoint) end
Step by step, with API calls
-
The call arrives and Pindrop starts analysing it
The caller dials in; the SBC forks the audio to Pindrop over SIPREC. When the call is answered — by a live agent, an IVR, or a virtual agent — Pindrop pushes a notification to Confirm containing a unique Call ID, the value exposed to you as
voiceId. Confirm creates the call record. No API call from you.Everything after this point is keyed on the
voiceId, so your first job is to get hold of it — see Getting thevoiceIdbelow. -
Assign the caller to the call
POST
/external/calls/assign— bind the in-progress call to the member you have on the line (the identity claim). Send eithervoiceIdorphoneNumber, plus the member'spersonId. Confirm resolves the call, records the claim, and correlates the account with Pindrop using the account alias{institutionKey}-{personId}.Requestcurl -X POST https://api.getconfirmed.org/external/calls/assign \ -H "SsoToken: <sso-token>" \ -H "InstitutionKey: acme-cu" \ -H "RequestedBy: jdoe" \ -H "Content-Type: application/json" \ -d '{ "voiceId": "b1f0…9c2e", "personId": "00123" }'Response 200{ "success": true, "voiceId": "b1f0…9c2e" }Send one identifier, not bothIf
phoneNumberis present it wins and Confirm assigns by phone number; otherwisevoiceIdis used. With neither, the response is{ "success": false, "error": "Please provide a phone number or voice id" }. The returnedvoiceIdis the handle for every later call — keep it for the duration of the call. -
Read the latest authentication results
GET
/external/calls/{voiceId}/details— Confirm fetches the current authentication results from Pindrop and returns the full call record: the applied policies, the enrollment / verification flags, risk signals, and the Pindrop event log. This is the call you poll while the call is live.Requestcurl "https://api.getconfirmed.org/external/calls/b1f0…9c2e/details?checkVoiceAuthentication=true" \ -H "SsoToken: <sso-token>" \ -H "InstitutionKey: acme-cu" \ -H "RequestedBy: jdoe"Response 200 (abridged){ "success": true, "voiceId": "b1f0…9c2e", "personId": "00123", "isVoiceEnrolled": true, "isVoiceVerified": true, "isVoiceMismatch": false, "isHighRiskDevice": false, "hasVoiceMatchValidationRule": true, "status": "FullAuthentication", "metaData": { "pindropPolicies": ["Green Device And Voice Match"] }, "pindropEvents": [ … ] }Drive your caller-handling logic offmetaData.pindropPoliciesTurn the policies into a single status — a traffic light for a human agent, a branch for an automated flow — using the keyword rules documented under Interpreting the validation status. Never decide off the raw scores in
pindropEvents, which are only present on Pindrop's call-end events. -
Enroll the voice once your system has verified the caller
POST
/external/calls/{voiceId}/enroll— call this when your system has established the caller's identity another way: knowledge-based questions, a one-time passcode, or a live agent's judgement. Confirm records the verification, sends Pindrop a SUCCESSFUL account-interaction feedback for{institutionKey}-{personId}against thisvoiceId, applies the Green Manual Authentication policy, and writes theAcceptCallerVoiceandFullAuthenticationactivities.Requestcurl -X POST https://api.getconfirmed.org/external/calls/b1f0…9c2e/enroll \ -H "SsoToken: <sso-token>" \ -H "InstitutionKey: acme-cu" \ -H "RequestedBy: jdoe" \ -H "Content-Type: application/json" \ -d '{ "personId": "00123", "validationType": "KnowledgeBasedAuth", "notes": "Verified by KBA" }' → { "success": true }This is what lets Pindrop keep the voiceprint, so the next call from this member can be voice-authenticated passively. Full detail in How voice enrollment works.
Already voice-verified? You don't need to call/enroll.When
…/details?checkVoiceAuthentication=truecomes back with a green Pindrop policy, Confirm has already recorded the authentication as part of serving that request — it writes theFullAuthenticationactivity, marks the call voice-validated, and records aVoiceAuthverification against the member (pushed through to the core). Nothing further is required from you./enrollexists for the case the passive check can't cover: a first-time caller with no voiceprint yet, or a call where the voice didn't match and your system verified the caller some other way. Calling it on an already-green call isn't rejected, but it records a second verification and re-sends the Pindrop feedback, so skip it.The other branchIf the caller can't be trusted, POST
/external/calls/{voiceId}/reject-voiceinstead. It sends Pindrop a FAILED interaction, recordsRejectCallerVoice, and un-enrolls every prior manual authentication for that member. See Rejecting & un-enrolling a voice. -
The call ends
When the call hangs up, Pindrop posts its call-ended event to Confirm with the final call analysis — voice score, device risk, liveness — and Confirm settles the call record and its policies. No API call from you; a later
…/detailsread reflects the final state.
Call summary table
The whole reseller surface used by this flow, in order.
| # | Call | When | You get back |
|---|---|---|---|
| 0 | POST /external/token/request |
Once, then refresh on 401 (30-minute sliding expiry) |
token — the SsoToken for every call below |
| 1 | GET /external/calls/queue |
To find the live call and its voiceId — the usual starting point |
phoneCalls — active calls, each with voiceId, phoneNumber, and candidate members |
| 2 | POST /external/calls/assign |
As soon as you have an identity claim | voiceId — the handle for this call |
| 3 | GET /external/calls/{voiceId}/details |
Repeatedly, while the call is live | Policies, flags, risk, Pindrop events |
| 4a | POST /external/calls/{voiceId}/enroll |
The voice hasn't authenticated on its own and your system verified the caller another way. Not needed when …/details is already green. |
{ "success": true } — voiceprint trusted |
| 4b | POST /external/calls/{voiceId}/reject-voice |
The caller could not be trusted | { "success": true } — voiceprint discarded |
Timing & retry notes
- Assign early. Pindrop's analysis is keyed to the account alias, so the sooner the identity claim is correlated, the more of the call Pindrop can score against the right member.
- Poll, don't wait. A Voice Pending status means Pindrop is still evaluating. Re-fetch
…/detailsuntil it resolves or the call ends — results arrive mid-call, not all at once. PasscheckVoiceAuthentication=true: withfalse, Confirm returns the stored record and never asks Pindrop, so the status can never move. - Polling stops on its own. Confirm stops querying Pindrop for a call once it is fully authenticated, the voice has been rejected, a red policy is applied, the call has ended, or after 20 authentication checks. Past that point
…/detailskeeps serving the stored result, so a loop left running won't burn Pindrop requests — but it also won't change. - Enroll only after the caller was actually verified. The enroll call tells Pindrop to trust this voiceprint on future calls; issuing it for an unverified caller poisons later authentications for that member.
- Branch on
success, not the HTTP status. Every response carries{ "success": bool, "error": "…" }. A401means the SSO token lapsed — re-run the service-account login.