Call_Detail__c record on the Lead.
This is a different integration from the Yotel for Salesforce managed
package, which is a Lightning Web Component
for a human to click “Push to Yotel” from inside the CRM. This page
covers the API-driven path — no LWC, no browser click, your own
automation calls the REST API directly (or bulk-pushes a report/list
view export on a schedule).
1. Mint an API key
- Log into
app.yotel.inas a tenant admin (api_keys:managepermission). - Settings → API Keys (
app.yotel.in/settings/api-keys) → Generate Key. - Pick the live environment. For scopes, either leave everything
unticked (full access) or tick at least
leads:write— the scope this integration pushes leads with. - Copy the
yt_live_…value immediately — it’s shown once; Yotel stores only a hash.
yt_test_… key first if you want to validate the integration
without dialing the PSTN — see Quickstart and
Authentication.
2. Push leads
Push a Salesforce Lead using its own field names — Yotel acceptslead_id / first_name / mobile as aliases for our canonical
salesforce_lead_id / name / phone, so there’s no field-mapping
step on your side. Full reference: Leads.
This is the same endpoint the Leads reference
documents, called with the CRM spellings. The two pages are not two
APIs. Either spelling works on every request; if you send both, the
canonical name wins. Responses always use the canonical names.
lead_id (salesforce_lead_id) does two jobs beyond round-tripping to
Call_Detail__c.Lead__c later: it’s a second dedup key alongside the
phone number, so a re-push of the same Lead with a corrected number is
still recognized as the same lead rather than double-dialed, and it’s
how Yotel resolves the Lead when writing the call outcome back (§4).
Single vs. bulk handle a duplicate differently. A single push
(POST /leads) that matches an existing lead in the campaign — by phone
hash or by salesforce_lead_id, either is enough — is rejected
outright with 409 Conflict:
id there is the id of the existing Yotel lead (not the one you
just tried to push); salesforce_lead_id is its existing SF id, or
null. No phone number appears in the request or the 409 body.
A bulk push (POST /leads:bulk) never 409s for this reason — a
duplicate row is counted in duplicates and reported as
{"lead_id": ..., "phone": ..., "status": "duplicate"} in results
alongside every other row, so one bad row in a 500-lead file doesn’t
fail the batch. Full shapes for both: Leads.
3. What happens on the call
Once the lead is pushed, Yotel’s dialer places the call and bridges the answered leg into the configured voice agent.name (first_name) you
pushed is forwarded automatically as a {{lead_name}} prompt variable —
so if your agent’s greeting template says “Hi , this is…”,
the bot greets the caller by the name you sent, no extra config needed.
salesforce_lead_id is forwarded the same way, in case your agent’s
prompt or tool-calls need it mid-conversation.
Personalization is additive: if you also push
"metadata": {"variables": {...}} with your own keys, those are
forwarded too and take precedence over the lead_name /
salesforce_lead_id defaults for any key you set explicitly.4. Call outcomes in Salesforce
When the call ends, Yotel writes aCall_Detail__c record (create, or a
PATCH if one already exists for that call — writeback is idempotent, so
a retried delivery never duplicates the record):
The one thing your Salesforce admin needs to do: grant the
connected app’s integration user Read, Create, and Edit on
Call_Detail__c — not Create alone — plus edit field-level security
on the fields above. Yotel needs all three because writeback isn’t a
one-shot create: before writing, it queries Call_Detail__c by
Call_ID__c to check whether a record already exists for this call
(idempotency — Read), and after the initial create it PATCHes the
same record once the recording finishes processing, typically a minute
or two later (Recording_File__c — Edit), plus on any retry of a
transient failure (Edit again). Read on Lead is also required, but
that’s standard — your integration user already has it. Yotel’s account
team configures the connected-app credentials on our side during
onboarding — the SF-side grant above is the only action item on yours.
Until the full grant is in place, writeback attempts fail visibly (not
silently): they show up as failed jobs your Yotel account team can see,
and the call itself, the recording, and the call.ended webhook (§5)
are completely unaffected — call outcomes just don’t reach Salesforce
until the permission lands.
5. Subscribe to call outcomes
If you want to write your own records instead of (or in addition to) relying on theCall_Detail__c writeback, subscribe to the webhooks
directly:
call.ended— fires the moment a call finishes, before the recording is ready. Carriescall_id,salesforce_lead_id,status,hangup_cause, durations,disposition, and arecordingobject (available: falseat this point).call.disposition_set— fires when the behavioural outcome is recorded (Interested, Site Visit, Do Not Call), which is what lands inSub_Dispoisition__c. It also fires again on a later correction, carryingprevious_disposition— so if you keep your own copy of the outcome, this is the event that keeps it right rather than leaving you with the first answer forever.call.recording_ready— fires 30–120s later withrecording.available: trueand the same non-expiringrecording.urlthat Yotel writes toRecording_File__c.
Call_Detail__c writeback, is configurable per campaign — see
Event routing.
Full payload shapes: Event catalog. Set up
the subscription at Developer → Webhooks and verify deliveries with
Signature verification — every
delivery is signed so you can confirm it came from Yotel before trusting
the body.
call.ended carries no phone number and no lead name — join on
lead_id or salesforce_lead_id against your own Lead record for
contact details. This is deliberate: it’s what keeps the event safe to
log and safe to deliver regardless of your tenant’s privacy mode.6. Privacy
If your tenant is configured for strict PII handling (the default we set up for a Salesforce-integrated tenant, since the number’s system of record is your CRM, not ours):- The phone number is stored encrypted at rest from the moment you push it. It’s decrypted in memory only for the instant it takes to place the call, and never written back to the database in plaintext.
- Every API response you receive — the single-push response, every row
of a bulk response,
GET/list calls — echoes a masked number (••••3210) instead of the plaintext, for the lifetime of the lead. - The number is purged from the database entirely — enforced at the database layer, not by a code path that could be skipped. A lead that opts out (DND) is purged the moment it does. A lead that finishes (completed, or exhausted its retries) keeps its number for the tenant’s retention window (default 30 days, configurable per tenant) so the campaign can be re-churned or cloned, and is purged by a nightly sweep after that.
- The number is never written to application logs, on any call path.
- The voice agent receives the number for the duration of the call
only, so it can send a WhatsApp message to the person it is speaking
with. The Zetta voice agent holds it in memory, never stores it, and
masks it (last 4 digits) in every log and in its post-call report. For
personalization it uses
lead_name/salesforce_lead_id(§3), not the number.

