Voice Profiles, Capabilities, and Session Context
View SourceOpenAI native realtime sessions can be configured with a code-defined profile.
The profile is the single source of truth for the assistant's persona, concrete
tools, session context, and security policy. Synaptic compiles it into Realtime
instructions and function schemas; the same capabilities can also become Luna
tools with Synaptic.Voice.Capability.to_tool/2.
Define a capability pack
defmodule MyApp.Voice.CalendarPack do
@behaviour Synaptic.Voice.CapabilityPack
alias Synaptic.Voice.Capability
def capabilities do
[
Capability.new!(%{
name: "calendar_list_events",
description: "List the caller's calendar events in a date range.",
parameters: %{
type: "object",
properties: %{
starts_at: %{type: "string"},
ends_at: %{type: "string"}
},
required: ["starts_at", "ends_at"],
additionalProperties: false
},
handler: &MyApp.Calendar.list_events/2,
risk: :read_only,
required_scopes: ["calendar:read"],
limitations: ["does not create or modify events"]
})
]
end
endA direct handler may accept arguments or arguments, session_context. Tool
names must be unique after all packs and individual capabilities are combined.
Individual capabilities may be added with :capabilities or removed with
:disabled_capabilities.
Define the assistant profile
defmodule MyApp.Voice.AssistantProfile do
alias Synaptic.Voice.Profile
def profile do
Profile.new!(%{
id: :customer_assistant,
persona: %{
name: "Ava",
role: "customer scheduling assistant",
purpose: "help the caller understand and manage their schedule",
tone: "warm, concise, and professionally conversational",
languages: ["en", "sk"],
instructions: ["Confirm dates and time zones when ambiguous."],
limitations: ["Never claim an appointment exists without a tool result."]
},
capability_packs: [MyApp.Voice.CalendarPack],
context_schema: %{
customer_name: %{classification: :safe, type: :string},
timezone: %{classification: :safe, type: :string, required: true},
customer_id: %{classification: :internal, type: :string}
},
security_policy: %{
allowed_scopes: ["calendar:read", "calendar:write"]
}
})
end
endContext classifications are:
:safe: may be included in model instructions.:internal: available to handlers but never placed in model instructions.:restricted: available only to application code and handlers, never placed in model instructions.
Supported context types are :string, :integer, :number, :boolean,
:map, and :list. Undeclared fields, invalid types, and missing required
fields reject session startup. Context lives only in the session process;
Synaptic does not persist it.
Start an authorized session
{:ok, %{session_id: session_id}} =
Synaptic.Voice.start_session(MyWorkflow, %{},
provider: :openai,
mode: :realtime,
experience: :realtime_2_1,
response_mode: :native,
profile: MyApp.Voice.AssistantProfile,
session_context: %{
customer_name: "Maya",
timezone: "Europe/Warsaw",
customer_id: "cust_123"
},
session_authorization: %{
capabilities: :all,
scopes: ["calendar:read"]
}
)Supplying profile: automatically selects experience: :realtime_2_1 unless
an explicit experience is provided. Keeping the experience visible is
recommended so the application records its intended compatibility contract.
Calls without a profile or experience retain legacy orchestrated behavior.
Only capabilities allowed by both the profile policy and session authorization are exposed to Realtime. The execution gateway independently checks capability authorization, required scopes, argument schemas, and confirmation before calling application code. Model output cannot bypass that gateway.
Confirmation for consequential actions
The framework always requires confirmation for :external_action,
:sensitive, and :destructive capabilities. Applications can tighten this
with confirmation: :always or security_policy.force_confirmation; they
cannot weaken the framework floor.
When :capability_confirmation_required is emitted, collect an explicit user
confirmation in the application and grant a single retry:
:ok = Synaptic.Voice.approve_capability(session_id, "calendar_create_event")Approval applies to the next invocation of that capability only. It is not stored across calls or sessions.
Security boundaries
- Keep authentication and source-system authorization in application code.
- Give every capability the narrowest required scopes and accurate risk level.
- Put secrets and stable customer identifiers in
:internalor:restrictedcontext, never:safecontext. - Treat model-visible context as untrusted data; never place executable instructions in customer fields.
- Return structured tool results and claim success only after the external system confirms it.