Salesforce-native auto companion prototype¶
Status: Current | Last reviewed: 2026-09-29
Use this handoff to develop the Salesforce-only Add a Driver / Add a Vehicle prototype. Agentforce uses Apex actions, private Salesforce records hold shared state, and an authenticated LWR Experience Cloud site displays the ACG forms. The original AWS/MCP demo remains a separate agent.
Explanation to share¶
The voice agent and the customer's form read and update the same Salesforce records. Agent Script opens a component, accepts spoken field updates, and runs bounded status checks. The customer signs in through an ACG verification page, sees the form in LWR, and submits directly. A subsequent action check returns the stored receipt, so the agent can acknowledge submission without the customer saying “done.” Salesforce enhanced messaging delivers the private link once its channel and template are ready.
Deployed prototype¶
| Item | Value |
|---|---|
| Org alias | ccaas |
| Native agent | TrueServe Auto Voice Salesforce Native, TS_Auto_Voice_SF_Native, v4 |
| Original agent | TrueServe Auto Voice Demo, TS_Auto_Voice_Demo, v5 retained |
| Experience site | TS Auto Companion, enhanced LWR, Live |
| Site path | /tsauto on the org's Experience Cloud domain |
| Verification page | TSAutoVerify, on the same site's Visualforce path |
| Form catalog | Existing acg-auto fields, logo and styles |
| Messaging component | TS_Auto_Companion_Link |
| Sending gate | TS_Native_Auto_Config.Default.MessagingEnabled__c, enabled in ccaas on September 28; checked-in deployment default remains false |
| Tester email gate | EnableTestEmail__c, enabled in ccaas on September 29; checked-in deployment default remains false |
The deployment package contains
Apex, Agent Script, objects, permissions, site metadata, components and validation
evidence. Test-recipient email, mobile number and private links are not fixtures
in source control. The dedicated test customer has a role-based Customer
Community Plus user and the TS_Auto_Native_Customer permission set.
Components and data flow¶
flowchart LR
caller["Customer on a voice call"]
message["Enhanced WhatsApp / SMS<br/>approved notification component"]
verify["ACG Visualforce verification<br/>Salesforce email code"]
app["Authenticated LWR app<br/>ACG driver / vehicle forms"]
subgraph platform["Salesforce"]
agent["Agentforce Voice<br/>Agent Script"]
actions["Typed Apex actions<br/>connect / open / patch / check / submit"]
identity["Portal User + Contact<br/>ANI lookup or dedicated tester Email"]
state["Private experience record<br/>versioned component records"]
messaging["sendConversationMessages<br/>consent enforced"]
end
caller <-->|Speech| agent
agent <-->|Actions and results| actions
actions --> identity
actions <--> state
actions --> messaging
messaging --> message
message -->|Private link| verify
caller -->|Enter code| verify
verify -->|Authenticated redirect| app
app <-->|Apex reads every 2 seconds<br/>validated edits and submission| state
identity -->|Intended user and sharing| state
Diagram source: agentforce-native-auto-companion.mmd.
| Component | Responsibility |
|---|---|
TSAutoNativeConnect |
Resolve ANI/customer or tester Email, create experience, optionally request link delivery |
TSAutoNativeIdentity / TSAutoNativeTesterProvisioner |
Select a dedicated tester or create its Contact and queue User/permission creation |
TSAutoNativeOpen |
Open Driver or Vehicle in the existing experience |
TSAutoNativePatch |
Apply only the spoken fields, validate them, advance revision |
TSAutoNativeCheck |
Return current login/form status and any stored receipt immediately |
TSAutoNativeSubmit |
Submit spoken values after explicit demonstration acknowledgement |
TSAutoNativeService |
Identity, authorization, locks, validation, revisions and receipts |
TSAutoNativeUi |
Authenticated LWR attach/read/save/submit/cancel endpoints |
TSAutoNativeAuth / TSAutoNativeLoginController |
Bound login destination and native credential/code verification |
tsAutoCompanion |
Form rendering, local editing, refresh, conflict review and submission |
The intake exposes an ANI-bound connection path when ANI is present and a
number-collection path when it is absent. Both call the same typed Connect
operation; the ANI path binds the alternate-number input to an empty string.
A messaging failure does not request another number. All connection/form
availability checks handle both null and empty experience IDs, so a
phone_required response can recover after the customer supplies a number.
Each Agent Script action has a distinct Apex invocable target. Reusing one
generic invocable target for differently described actions did not reliably
select the intended operation in authoring preview. The generic
TSAutoNativeActions router remains useful for operator/API validation.
Shared state and authorization¶
TS_Native_Auto_Experience__c is the session record. It holds the intended
CustomerUser and Contact, optional VoiceCall and MessagingSession relationships,
active workflow, revision, expiry and login state. Only the SHA-256 digest of the
random 256-bit URL locator is stored. The raw locator is returned once in the
private link. Possession of the link does not authenticate the customer.
TS_Native_Auto_Component__c is a child record per experience/workflow. It holds
values JSON, revision, source, submission status, timestamp and receipt. Driver
and Vehicle use the same experience and link. Switching the active workflow
does not erase the other component.
The parent is private. The prototype grants the intended portal user a record
share and checks UserInfo.getUserId() against CustomerUser__c on every app
operation. A different user is rejected even if a record share exists. Guest
users receive no object CRUD or form data. Customers have read-only object
permissions; writes pass through the Apex validation and revision checks.
The service explicitly performs custom-state DML/queries in system mode after its authorization checks. Do not copy a method into an unrestricted invocable or grant customer users the agent custom permission. Agent runtime access and operator inspection use separate permission sets. The operator set is for administrators, not the voice runtime or portal users.
With Email blank, ANI locates a candidate identity; it is not authentication. The prototype scans up to 200 active portal users and requires exactly one normalized phone match. Use an indexed identity mapping before scaling. The current sharing approach requires a role-based external license; high-volume users need a sharing-set design before they can use it.
Optional tester Email¶
In native agent v4, an operator can set ANI and Email before starting a
fresh Builder preview. No tester Name is required. Both connection actions bind
Email from session state; the conversation cannot supply or change that action
input. The driver's full_name remains an independent form field.
When Email is supplied, it selects only an active portal user on the dedicated
TS Auto Native Customer profile. An unknown email creates a Contact named
Auto Tester under the configured tester Account and queues creation of its
Customer Community Plus User and TS_Auto_Native_Customer permission assignment.
An internal user or a user on another profile with the same email is never
selected. Duplicate or inactive dedicated testers require an operator to resolve
the mapping.
ANI is then only the WhatsApp destination. It is not copied to the tester User or Contact. Changing ANI does not change the tester identity or the existing Messaging End User's Contact relationship. Channel opt-out remains enforced. Leaving Email blank preserves the original unique ANI lookup.
EnableTestEmail__c must be true and TestAccountId__c must identify a dedicated
Account whose owner has a Salesforce role. The demonstration org uses
TrueServe Native Auto Testers. Each new email consumes an available external
user license; deactivate disposable testers when finished. Keep this mode
disabled outside an explicitly configured test environment.
Contact/experience DML and User/PermissionSetAssignment DML occur in separate
transactions to avoid Mixed DML. A locked Account serializes contact creation;
repeated requests reuse a queued job or an existing tester. The experience stores
TestEmail__c and TesterProvisionJobId__c while CustomerUser__c is pending.
On a protected login POST, the server resolves that exact Contact/email pair,
sets the intended User and creates its record share before exposing any form
data. The ACG page checks pending account preparation every two seconds, up to
ten automatic attempts, without consuming login-code attempts. A failed job
shows an operator setup message; Send email code can retry a still-pending
account after the automatic checks end.
Email selects the identity; it does not redirect another user's OTP.
Salesforce sends its native code to the selected User's registered email.
First-use email ownership verification still applies. In CLI authoring
Live Test, the tested initial variable names are Email and $Context.ANI;
$Variable.Email and $Context.Email do not populate the mutable Email variable.
When supplied, VoiceCallId is validated through user-mode access and associated
with the experience. LWR receives a safe call summary, not unrestricted access
to the VoiceCall object or transcript. Builder tests may omit the call ID.
Passwordless login¶
- Delivered messages open the administrator-configured ACG Visualforce
verification page directly. This avoids routing a new customer through
the authenticated LWR home page first. The action's internal
appUrlremains the LWR destination for preview clients. Older LWR links still usetsAutoLoginto reach the same verification page when sign-in is needed. - The page initiates
UserManagement.initPasswordlessLogin(userId, EMAIL)in a normal Visualforce POST after its signed form state is available. Salesforce sends the email code and returns a challenge identifier. The identifier stays in the server-side experience record. - The customer enters the code on the ACG page. The controller calls
UserManagement.verifyPasswordlessLogin(userId, EMAIL, identifier, code, startUrl). On success, it follows Salesforce's returned redirect into the LWR form. - For a newly created user, the email must first be verified. The page offers
sendAsyncEmailConfirmationto send Salesforce's initial verification link, then lets the customer request a sign-in code. A portal password remains a fallback. Neither passwords nor codes are requested by the voice agent.
Salesforce explicitly documents the paired passwordless methods for
Visualforce Login/Verify pages. Calling them directly from the original LWR
Aura controller returned no challenge identifier in this org. The dedicated
test user also had HasUserVerifiedEmailAddress=false, a separate prerequisite.
Guest browser checks received a native challenge identifier and displayed the
ACG email-code field, including a mobile browser check on September 28.
Customer-controlled code verification and the return to authenticated LWR still
need acceptance. Do not
treat an administrator's Login As test as evidence that passwordless login works.
The WhatsApp URL carries a locator, not the OTP, challenge identifier, username
or VoiceCall ID. This implementation is not a one-tap WhatsApp magic login.
initPasswordlessLogin does not return the generated code. Salesforce also has
Auth.CustomOneTimePasswordDeliveryHandler for an external SMS provider; that
is a different delivery extension and does not establish native WhatsApp
magic-link support for this prototype.
The page does no DML on GET. Code/email requests use Visualforce POST protection, per-link attempt limits and email throttling. Responses disable caching and referrers. OTP/password fields are transient, and caller-supplied return URLs are ignored. Both the link and data access expire after one hour. There is no scheduled record-retention cleanup job in this prototype.
Login IP ranges¶
The public guest profile TS Auto Companion Profile and the dedicated portal
profile TS Auto Native Customer must support customers on mobile networks.
On September 28, both had a LoginIpRange row labelled ALL, spanning only
0.0.0.0 through 255.255.255.255. The site's public hostname also resolved
to IPv6 addresses, while the diagnostic workstation could connect only over
IPv4. The two prototype-profile rows were removed; queries verified zero
remaining login ranges on each profile.
The customer then confirmed that the same mobile link opened the ACG code-entry page. That validates resolution of the entry-page failure in the customer's retest. The connection's actual address family was not measured. Authentication and the intended-user check remain required for form data.
Check Setup → Profiles → [the dedicated profile] → Login IP Ranges after
provisioning. Do not assume that a row labelled ALL means no restrictions.
Omitting loginIpRanges from a profile deployment does not serve as the
operational check for existing rows: query LoginIpRange by that profile's
ID and explicitly remove an unwanted row. Do not change unrelated profiles
or org-wide Network Access settings.
Native messaging¶
The org's AgentforceTesting_Rahul channel is PstnVoice, not an SMS channel.
Do not create a Text record with its existing phone number and assume SMS has
been provisioned. The configured alternative is the existing active enhanced
WhatsApp channel WHATSAPP_US_1317140281476081.
TS_Auto_Companion_Link is a Salesforce-active notification component with a
WhatsApp Utility template. The first external review was rejected without a reason exposed by Salesforce.
A revised request-specific notification was resubmitted and its external approval
was Pending at the September 23 validation. On September 28, the org's sending
gate was enabled and the customer confirmed receiving a WhatsApp link. The
corresponding Messaging Session contains the outbound notification.
Keep the checked-in default disabled until each target org's channel/template
is usable. Existing inbound channel routing is unchanged.
TSAutoNativeMessaging invokes Salesforce's standard
sendConversationMessages action with:
messageDefinitionName=TS_Auto_Companion_Linkand thecompanionUrlparameter, now containing the direct ACG verification URL and the same opaque locator;requestType=SendNotificationMessages;isEnforceMessagingChannelConsent=trueandchannelConsentType=MessagingEndUser;allowedSessionStatus=AnyandsessionLongevityPreference=KeepSessionOpenOrAppend.
The action returns requestId and messageIdentifiers, not a MessagingSession
ID. A queued request is not proof of handset delivery. The prototype stores the
request ID and links a session only when the exact MessagingEndUser has one
unambiguous current session. It does not guess the latest conversation.
WhatsApp end-user keys use digits; SMS phone numbers use E.164. Existing opt-outs
and conflicting Contact associations are not overridden.
Voice and form concurrency¶
LWR polls the authenticated Apex read endpoint every two seconds while visible. Typing marks a field dirty immediately. A newer voice revision does not silently replace local edits: the page offers Use agent’s values or Keep my changes. Saving uses a revision compare under a parent-record lock. A stale workflow or revision is rejected. Editing clears the acknowledgement checkbox; completed requests are immutable, and identical repeat submission returns the same receipt.
Agent Script checks before reasoning and transitions into a maximum of 30
immediate Apex checks after reasoning while a form is open. These are fast
reads, with no long-running wait or sleep. One observed open-and-check turn took
21.765 seconds including reasoning; that is not a promised checking duration.
When delivery returns messaging_configuration_required, after-reasoning checks
are skipped and the agent offers voice continuation. It does not ask the customer
to open an unsent link or wait for sign-in.
A form-only submission is retained and acknowledged when a check observes it. After the check budget is exhausted, there is no documented native callback in this implementation that wakes an idle voice session. A later voice turn checks again. Authentication may take longer than one checking cycle. Do not promise indefinite monitoring or guaranteed speech interruption during action execution.
TS_Native_Auto_State__e is an optional after-commit internal notification. It
contains record/revision/workflow identifiers and no form fields. It does not
resume Agentforce Voice, and the LWR prototype uses polling rather than a
Platform Event subscription.
Validation and remaining acceptance¶
See the form integration evidence from version 2 and the version 3 ANI intake regression evidence. The version 4 tester-email evidence covers Email binding in authoring Live Test, automatic tester provisioning, guest resolution and sharing, and the combined Apex regression suite. The validated form path used authoring Live Test text utterances and a real portal browser session entered through administrator Login As:
- Driver and Vehicle shared one connection and private link.
- Agent actions updated fields from supplied conversational values.
- The authenticated page displayed those updates and submitted both forms.
- Each
check_nativereturnedcompleted_demo, and the agent acknowledged the receipt with no completion utterance from the customer. - A conflicting local edit was preserved until the customer chose a resolution.
- Apex tests cover intended-user enforcement, expired locators, revision and workflow conflicts, validation, acknowledgement and login guardrails.
The prototype still needs successful customer-controlled code verification and microphone-based Builder/PSTN/SIP acceptance. A successful text authoring test does not prove voice barge-in or production call behavior. Test coverage is reported in the evidence; the prototype is not presented as a production deployment certification.
The September 28 login-routing evidence records the first customer-confirmed WhatsApp delivery and the move to direct verification links. The customer's reported standard Salesforce login screen was not reproduced in clean desktop/mobile contexts: the exact original link reached ACG verification. Direct links remove that intermediate site-login route; they do not implement a WhatsApp magic login or prove successful OTP completion.
The customer subsequently reported an immediate “too many redirects” error on a direct verification link, including mobile Chrome. The same delivered link returned HTTP 200 for both the page GET and the email-code POST in a clean browser. The IPv4-only login ranges described above were then removed from the two dedicated profiles. The customer confirmed that the same mobile link then opened the ACG code-entry page. Completing code verification and returning to the authenticated form still need customer acceptance.
Primary references¶
- UserManagement: passwordless verification and initial email confirmation
- Auth.VerificationResult
- TwoFactorMethodsInfo and its Manage MFA in API permission requirement
- CustomOneTimePasswordDeliveryHandler
- Salesforce agentic integration patterns