Skip to content

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

  1. 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 appUrl remains the LWR destination for preview clients. Older LWR links still use tsAutoLogin to reach the same verification page when sign-in is needed.
  2. 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.
  3. 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.
  4. For a newly created user, the email must first be verified. The page offers sendAsyncEmailConfirmation to 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_Link and the companionUrl parameter, now containing the direct ACG verification URL and the same opaque locator;
  • requestType=SendNotificationMessages;
  • isEnforceMessagingChannelConsent=true and channelConsentType=MessagingEndUser;
  • allowedSessionStatus=Any and sessionLongevityPreference=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_native returned completed_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

See also