Configuration
One configuration object drives the whole experience. The model is identical on iOS, Android, and Flutter — examples below are Swift; the other platforms use the same names.
Theming
One VoqalTheme drives every surface. Hairlines, text tiers, and fills are derived automatically so the assistant looks native in light or dark.
config.theme = VoqalTheme( accent: "#2d5bff", // your brand color (hex) accent2: "#5b8dff", // optional gradient pair appearance: .auto, // .light · .dark · .auto (follows the phone) font: nil, // your app's font — one font for ALL text radius: 20) // base corner radius for cards & the sheetPresentation style
The default .sheet slides up and is swipe-to-dismiss; .fullScreen takes the whole screen edge-to-edge and dismisses via the close button. This is a setup-time setting.
config.presentationStyle = .fullScreen // default: .sheetHeader branding
Replace the default “Voqal” title and mark with your own. Leave both unset to fall back to the Voqal title and mark.
config.strings.chatHeaderTitle = "Rabbit"config.icons.chatHeaderIcon = UIImage(named: "RabbitLogo")Action button
Optionally show an accent-colored button next to the voice wave that takes the user to a page in your app — a checkout screen, for example. The SDK renders the button; your app owns the route. Off by default.
// Enable the button (nil icon → a default shopping-bag glyph).config.actionButton = VoqalActionButton(icon: UIImage(named: "Cart"))// Navigate from your own view controller when it's tapped.func voqalDidTapActionButton() { let checkout = CheckoutViewController() getViewController().present(checkout, animated: true)}dismissOnTap: false, then calls you back.Redirect & checkout
There is no single “checkout button” setting — sending a user to a payment or a destination happens one of three ways, depending on who owns the flow. Pick the one that matches where the action lives.
| Mechanism | Who renders it | Carries a payload? |
|---|---|---|
| Action button | SDK chrome, your app navigates | No — a plain tap into your own flow |
| Confirm widget | Engine emits it, SDK executes it | Yes — title, price, CTA, action |
| Automatic link card | SDK, from a URL in the answer | Yes — the URL itself |
- Action button — the accent button next to the voice wave (see
config.actionButtonabove). On tap the SDK calls yourvoqalDidTapActionButtoncallback and your app pushes its own checkout screen. It carries no conversation payload; use it for a fixed destination like a cart or a settings page. - Confirm widget— for an action the agent proposes in-conversation (“create a 500 EGP payment link”), the engine emits a single
confirmcard with the title, amount, and call-to-action. The user confirms it and the SDK runs it via/voqal/execute. You render nothing and author no card — the engine owns the copy and the payload. - Automatic link card — any
http(s)URL in the agent’s answer becomes a tappable card the SDK opens with the OS URL handler. Return a checkout link from your backend and it is presented automatically — no widget, no config.
requiresBiometric, so the SDK gates it behind Face ID / Touch ID before executing; low-risk actions are tap-to-confirm. The engine decides the tier per action — you do not configure it, and the agent never names the method aloud.Forwarding headers to your backend
Two header channels leave the SDK, and they go to different places — this is the distinction to get right. One is read by the Voqal engine; the other is passed straight through to your backend untouched.
| Channel | Read by | What it carries |
|---|---|---|
X-Client-Metadata | The Voqal engine | Opaque JSON context — country_code, user_id — for locale and personalization. |
X-Voqal-Forward-* | Your own backend / MCP | Verbatim passthrough. The engine strips the prefix and forwards the original header as-is. |
Metadatais set through the SDK’s metadata hook and never reaches your backend as a header; the engine consumes it. Forwarded headers (new in 2.0.0) are the seamless way to hand your backend whatever it needs to authenticate a request — no dashboard configuration. You return a plain map; the SDK sends each entry namespaced as X-Voqal-Forward-<Name>, and the engine strips the prefix and forwards <Name> to your backend on every call.
// Return the headers YOUR backend needs. Read live — called every request.func voqalForwardedHeaders() -> [String: String] { return [ "Authorization": "Bearer \(session.accessToken)", "X-Tenant-Id": tenantId, ]}The hook is named per platform, and each platform exposes both channels:
| Platform | Metadata hook (engine) | Forwarded-headers hook (your backend) |
|---|---|---|
| iOS | getMetaData() | voqalForwardedHeaders() |
| Android | getMetadata() | getForwardedHeaders() |
| Flutter | metadataJson | forwardedHeaders |
| React Native | metadataJson | forwardedHeaders |
- Read live, every request — the hook is called on each turn, so a rotated or refreshed token is always current. Never cache a stale value.
Authorizationis forwardable — return it like any other header and your backend receives it asAuthorization.- Reserved control headers are dropped —
X-Token,X-Voqal-Key,X-Request-ID,X-Client-Metadata,Content-Type, andAcceptcan never be overridden by a forwarded header. - Bounded — at most 32 headers per request; excess is dropped rather than failing the turn.
- Destination-scoped— forwarded headers reach only your tenant’s own backend, never anywhere else.
Home screen
The greeting name, the “Try saying” suggestions, and whether the live data glance shows when the assistant opens.
config.home = VoqalHome( userName: "Nour", pinnedCTAs: ["What's my balance?", "Create a payment link"], showAgentGlance: true)Configuration reference
| Property | Type | Default | Description |
|---|---|---|---|
requestId | String | required | Environment routing — prod- or stg- prefix. |
apiKey | String | required | Your Voqal API key (pk_live_…), sent as X-Voqal-Key. |
theme | VoqalTheme | default | accent, accent2, appearance, font, radius. |
home | VoqalHome | empty | userName, pinnedCTAs, showAgentGlance. |
presentationStyle | PresentationStyle | .sheet | .sheet (slide-up) or .fullScreen (edge-to-edge). |
strings | StringsConfiguration | “Voqal” | chatHeaderTitle— the assistant’s name in the header. |
icons | IconConfiguration | accent orb | chatHeaderIcon, voqalButtonIcon. |
actionButton | VoqalActionButton? | off | Optional in-app navigation button next to the voice wave. |
conversationTimeout | TimeInterval | 7200 | How long a conversation resumes after close and reopen (seconds). |
agentURL | URL? | baked in | Override the engine endpoint (rarely needed). |
- Fast first turn — call
prewarmright aftersetup: it opens the engine connection in the background so the assistant is instant when opened. - Conversation memory —
conversationTimeout(default 2h) controls how long a conversation resumes after the sheet is closed and reopened.
