Skip to content

Agentforce Voice companion forms

Status: Current | Last reviewed: 2026-09-29

Use this handoff to explain or extend the Add a Driver / Add a Vehicle demo: Agentforce Voice manages the conversation, an AWS service manages the companion form, and Agent Script runs bounded short checks for the submitted result. The original TrueServe Auto Voice Demo (TS_Auto_Voice_Demo) is active on version 5 in the ccaas org as of the review date.

The Salesforce-native clone uses separate Apex actions, authenticated LWR forms and native messaging. This page describes the original AWS/MCP implementation.

Explanation to share

Agentforce calls an AWS-hosted MCP service through Apex actions. That service creates a shared experience, texts a private link, and renders the selected Auto Club Group form. The app posts its submission to the service. Agent Script opens the form immediately, then runs short status checks with deterministic transitions between them. A submitted result returns through the action and the agent acknowledges it without the customer saying “done.” New voice turns refresh form status and restart the bounded checking window.

Components and ownership

flowchart LR
    caller["Customer"]
    app["Companion web app<br/>ACG driver / vehicle forms"]
    subgraph salesforce["Salesforce"]
        voice["Agentforce Voice<br/>Agent Script v5"]
        apex["Apex invocable actions<br/>TrueServeAutoMCP credential"]
    end
    subgraph aws["AWS demo"]
        edge["Global Accelerator<br/>HTTPS ALB"]
        mcp["multimodal-service<br/>MCP + REST + SSE"]
        state["PostgreSQL: sessions<br/>Redis: forms, events, results"]
        sms["End User Messaging SMS"]
    end
    caller <-->|Speech| voice
    voice <-->|Action calls and results| apex
    apex <-->|OAuth MCP| edge
    edge <--> mcp
    mcp <--> state
    mcp -->|Send private link| sms
    sms -->|Text message| caller
    caller -->|Open link| app
    app <-->|Session-scoped REST and SSE| edge

Diagram source: agentforce-voice-companion-forms.mmd.

Component Responsibility
Agentforce Voice and Agent Script Voice dialogue, consent, workflow selection, bounded checks, receipt acknowledgement
Apex adapters Typed action inputs/outputs and synchronous MCP callouts through TrueServeAutoMCP
multimodal-service Connection links, SMS requests, form rendering, validation, submission results
Companion app Display forms and send authenticated submissions over REST; receive rendering events over SSE
PostgreSQL and Redis Session metadata in PostgreSQL; form state, events, deduplication and retained results in Redis

The deployed app is the standalone demo web app, served at /demo/. It uses the separate acg-auto catalog with ACGAddDriverForm and ACGAddVehicleForm. You can build a custom mobile client against the same REST/SSE contract; that client integration is additional work. The existing demo does not require a native mobile SDK.

The AWS demo runs the service, PostgreSQL and Redis in separate containers on one EC2 host behind Global Accelerator and the HTTPS ALB. It is a single-host demo with expiring state. See the deployment notes before reproducing it, including the current short-wait image requirement.

Connection and submission flow

  1. Resolve the mobile number. ANI is optional linked context from VoiceCall.FromPhoneNumber; Agent Studio/API callers can supply $Context.ANI. If absent, the agent asks for a mobile number including country code.
  2. Connect after consent. connect_experience creates the experience and requests an AWS text containing its private link. The agent stores the returned experienceSessionId internally. Customers never supply that ID. sms_submitted means AWS accepted the request, not confirmed handset delivery.
  3. Attach the app. The URL carries an opaque code in its fragment. The app redeems it at /v1/connections/redeem for a scoped browser JWT, then subscribes to /v1/sessions/{id}/events. MCP credentials stay on the servers.
  4. Open immediately. open_form renders the requested component and returns surfaceId and status with waitForForm=false. Driver and Vehicle reuse the same experience and link, with surfaces auto-driver and auto-vehicle.
  5. Check while the form is open. Agent Script runs the short-check cycle below. The agent can answer field questions on new voice turns.
  6. Submit from the app. The app sends the validated action envelope to POST /v1/sessions/{id}/ui-action. The service validates and retains the terminal result. An outstanding check returns it, or the next check reads it.
  7. Acknowledge the result. report_status summarizes the validated receipt. Neither a spoken completion cue nor a second Voice session is required.

The experience ID identifies shared form state. It is distinct from the Salesforce Voice session ID and the MCP transport session ID. Internal variables also hold the private appUrl, active surface, mode, result and check counter.

Salesforce action contract

Agent Script action Apex adapter MCP tool
connect_experience TSAutoMcpConnect connect_auto_experience
open_form / open_spoken_intake TSAutoMcpStart start_auto_change
check_form TSAutoMcpAwait await_auto_change
submit_spoken TSAutoMcpSpoken submit_spoken_auto_change

The tested execution path uses these Apex adapters and the TSAutoMcpClient helper. Although the MCP tools are registered in Salesforce, the current script targets apex://... actions. TrueServeAutoMCP supplies the OAuth-authenticated MCP connection. You need the corresponding runtime-user permissions described in the Salesforce setup guide.

The Agent Script pattern that worked

The full script separates conversational reasoning from deterministic checks:

  • auto_intake.before_reasoning resets the counter and reads any active app result immediately with waitSeconds=0.
  • auto_intake.after_reasoning transitions to monitor_form in app mode.
  • monitor_form.before_reasoning waits up to two seconds, then transitions directly to another check or report_status. It bypasses LLM reasoning between checks, avoiding repeated speech.
  • report_status acknowledges completed_demo or cancelled. At the limit, pending only means that the form remains open.

The checking block is an excerpt of the deployed script:

before_reasoning:
    run @actions.check_form
        with experienceSessionId = @variables.experienceSessionId
        with surfaceId = @variables.activeSurfaceId
        with waitSeconds = 2
        set @variables.formResult = @outputs.resultJson
        set @variables.formStatus = @outputs.status
    set @variables.checkCount = @variables.checkCount + 1
    if @variables.formStatus == "pending" and @variables.checkCount < 30:
        transition to @subagent.monitor_form
    else:
        transition to @subagent.report_status

Version 5 allows 30 checks per turn, giving roughly 60 seconds of wait budget plus action overhead. A two-second wait is not a two-second response guarantee. Each call remains synchronous, and speech has not been shown to cancel an individual call early.

Salesforce's reasoning-hook reference allows actions and transitions in these blocks. after_reasoning runs when reasoning exits; it is not a callback for the end of audio playback. Prompt instructions guide consent, questions and intake mode; the script controls the repeated checks.

The app submission happens independently of the conversation, but Salesforce receives it as an ordinary action result. This implementation does not use the outbound asynchronous action pattern to resume an idle Voice session. It needs no transcript observer, hidden audio, or join to an existing LiveKit room.

Boundaries and evidence

Silent submission is detected during the checking window. After the window, the result remains stored within the session lifetime, but a new voice turn may be needed to read it. This is not indefinite background monitoring or an idle Voice push callback.

Explicit spoken intake uses the same action contract: sendLink=false creates an experience without a text, and complete, confirmed details use submit_spoken_auto_change. Partial spoken fields do not update the visible form. Switching from spoken intake back to app entry calls open_form again to rearm checks. All receipts are demonstrations; no policy or coverage changes.

Validation on 2026-09-22 What the evidence establishes
Main demo v5, live authoring actions Agent-created experience; Vehicle submitted after 45 seconds; Driver submitted on the same experience; two receipts without completion speech
Main demo v5, published Voice entry Missing ANI prompts for a mobile number, not an experience ID
Diagnostic TS_Auto_Voice_MCP_Probe v6, published Voice In one call, a field question received an audio answer in about 4.1 seconds, then silent form submission received receipt audio in about 3.2 seconds
Salesforce validation Agent Script validated/published; 18 Apex tests passed for the main upgrade

The diagnostic used eight short checks per request and its own Agent API/LiveKit Voice connection. Its timings are approximate observations, not service limits. The main demo uses thirty checks; its delayed-form test used the live authoring SDK. Supplied ANI was checked with simulated actions. No real SMS was sent in these upgrade tests. The full current flow still needs authenticated Agent Builder, handset delivery and PSTN/SIP acceptance testing.

Recorded evidence: main customer flow and same-call Voice/form diagnostic.

See also