Checking your session…

Make It Yours — Settings, Voice & Web Chat

Out of the box your assistant is capable but generic. This chapter is where it becomes yours: its name, its greeting, how it sounds on the phone, how the chat widget looks on your website, which languages it opens in, and what it shows visitors. Everything here is configuration you set once and refine as you learn what your callers need.

All of these are app attributes set via PATCH /api/v1/apps/{appId} unless a different endpoint is shown. Remember: attribute changes are drafts until you take the assistant live (web-chat settings written via the dedicated endpoint below are the exception — they take effect immediately).

Identity & behaviour:

  • assistantName — display name in the chat UI
  • welcomeMessage — initial greeting
  • voiceChatPrompt — system instructions for conversations. Either a string (single prompt) or an object {"default": "...", "demo": "..."} for multi-mode prompts (selected via ?v=mode on the voice link)
  • voiceChatTools — JSON array of tool definitions the assistant can call
  • voice — voice option (case-insensitive). Valid: alloy (default), ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse

Appearance:

  • primaryColor, secondaryColor, tertiaryColor, textColor — hex codes. Legacy: for the web-chat widget these are fallbacks only — style the widget via webchat.branding (below), where one brand colour derives the whole palette.
  • font — font family. Legacy fallback for the widget; prefer webchat.branding.fontFamily. Supported webchat fonts: Manrope, Inter, Roboto, Open Sans, Lato, Montserrat, Poppins, Nunito, Source Sans 3, Work Sans, DM Sans, Karla, Merriweather, Playfair Display. Set webchat.branding.fontUrl to https://<widget-host>/widget/fonts/<slug>.woff2 (slug = the name lower-cased, spaces → hyphens) so the widget loads the face on any host page.
  • assistantAvatar, thumbnailImage — image objects {s3Key, url}
  • embedAppTitle — title in iframe-embed mode

Billing:

  • accountId — if set, the app draws from an account credit pool. (Also returned/accepted as the legacy alias merchantId — see the naming-transition note under For Partners.)
  • billingMode"app" (default) or "merchant" (pool-funded; see Billing & Credits)
  • pricePerMinute, allowanceMinutes, overageRatePence — per-app billing overrides
  • lowBalanceThreshold — alert threshold in seconds (default 600)

Voice tuning (see the voice attribute table below):

  • voiceEngine, turnDetectionType, vadEagernessPhase1, vadEagernessPhase2, vadThreshold, vadPrefixPaddingMs, vadSilenceDurationMs, noiseReduction, transcriptionModel, transcriptionLanguage, maxOutputTokens, pipelineConfig

These control how the assistant listens, transcribes, thinks and speaks on voice calls. The defaults suit most businesses; tune them when you have a specific problem (an assistant that interrupts, a noisy call environment, a non-English caller base).

Attribute Type Notes
voiceEngine string "openai" (default), "livekit"
turnDetectionType string "semantic_vad" (default), "server_vad", "disabled"
vadEagernessPhase1 string low / medium (default) / high / auto — speech-end detection during greeting
vadEagernessPhase2 string low / medium / high (default) / auto — during conversation
vadThreshold number 0.0-1.0, server_vad only (default 0.5)
vadPrefixPaddingMs number 0-2000, server_vad only (default 300)
vadSilenceDurationMs number 200-2000, server_vad only (default 500)
noiseReduction string near_field / far_field (default) / disabled
transcriptionModel string gpt-4o-transcribe / gpt-4o-mini-transcribe (default) / whisper-1
transcriptionLanguage string ISO 639-1 (e.g. en, es, fr). Default en
maxOutputTokens number 256-4096 or -1 for unlimited (default 4096)
voiceDisplayName string Spoken voice label, also used in voice greetings
isPreventKBAccessForVoiceChat string ("true"/"false") Default false

Two ways to set voice configuration: field-by-field via PATCH /apps/{id} (useful for small tweaks), or atomically as a single pipelineConfig blob — one write, internally consistent. Atomic is preferred when you're setting several values that belong together.

Requires edit_settings.

This is a draft change — take the assistant live to apply it.

POST/api/v1/apps/{appId}/voice/pipeline-configedit_settings
curl -X POST https://api.everycallhandled.com/api/v1/apps/$APP_ID/voice/pipeline-config \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "pipelineConfig": {
      "mode": "modular",
      "stt": { "provider": "openai" },
      "llm": { "provider": "openai", "model": "gpt-4o" },
      "tts": {
        "provider": "cartesia",
        "voice": "794f9389-aac1-45b6-b726-9d9369183238",
        "options": { "locale": "en-GB", "gender": "female", "speed": 1.0 }
      },
      "vad": { "type": "server_vad", "threshold": 0.5, "prefix_padding_ms": 200 },
      "multilingual": true,
      "noise_filter": "noizeless"
    }
  }'
Sign in to run this against your account.

Requires edit_settings. Returns the merged voice configuration (the pipelineConfig blob + individual fields + platform defaults) — what the assistant will actually use, after all layers resolve.

GET/api/v1/apps/{appId}/voice/configedit_settings
curl 'https://api.everycallhandled.com/api/v1/apps/{appId}/voice/config' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Sign in to run this against your account.

The web-chat widget is styled from a single webchat.branding object, written via the web-chat endpoint below. Set primaryColor and the platform derives the full widget palette (header, bubbles, chat button, contrast text). Optional hex overrides, each replacing its derived value when set and reverting to derived when empty: headerBgColor, headerTextColor, assistantBubbleColor, assistantTextColor, userBubbleColor, userTextColor, launcherColor, launcherIconColor. Plus surface (light|dark|auto), radius (0–24), position (bottom-right|bottom-left), logoUrl (https), launcherIcon (chat|logo|https URL), fontFamily/fontUrl (see the font list above).

Beyond branding, the widget reads a small set of conversational keys from the same webchat config object. All are written with:

Requires edit_settings. The body is { "webchat": { ... } } (a bare { ... } object is also accepted). The write validates then merges: keys you omit are left untouched, and sibling config (branding, workflows, the tools array) is preserved — toggling one key never wipes the others. GET /api/v1/apps/{appId}/webchat returns the normalised, stored shape. Changes take effect on the live widget immediately.

quickActions — up to 4 opening buttons shown on a fresh chat. Array of { "label", "message" }; tapping one sends message as the visitor's first turn. label is capped at 32 chars, message at 200; entries missing either field are dropped, and the list is capped at 4.

startLanguage — the language the assistant opens in. Values:

  • "" (empty) — English (the default).
  • "auto" — follow the visitor's browser / host-page language.
  • a language name (e.g. "Spanish") or ISO code (e.g. "es") — open in that language.

The 12 supported non-English languages are: French, Spanish, German, Italian, Portuguese, Polish, Romanian, Arabic, Turkish, Urdu, Hindi, Mandarin Chinese. An unrecognised value resolves to English. The embed snippet can override the app setting per-page with the data-ghost-chat-lang attribute (same auto / name / code semantics); the embed attribute wins over webchat.startLanguage.

Opening with an intent — the embed also accepts data-ghost-chat-intent: a message auto-sent as the visitor's first turn when the chat opens on a fresh thread (e.g. a "Book now" button whose embed carries data-ghost-chat-intent="I'd like to book an appointment" opens the chat already in the booking flow). Resumed conversations and threads with any prior visitor turn ignore it.

welcomeMessages — an object mapping language to welcome-bubble text (up to 12 languages; values capped at 500 chars). Resolution rule for the static opening bubble:

  • Default language (English / empty start language) — the app's standard welcome message.
  • Non-default language with a stored translation here — that translation.
  • Non-default language with no translationno static bubble; the assistant's first turn greets the visitor in-language instead.

emergencySignpost — plain text (up to 200 chars), e.g. "call 999 or NHS 111". Used by urgent escalation: when the office is closed and the visitor raises a genuine emergency, the assistant advises this pointer. Omit it and no emergency advice is appended.

PUT/api/v1/apps/{appId}/webchatedit_settings
curl -X PUT 'https://api.everycallhandled.com/api/v1/apps/{appId}/webchat' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"webchat":{"quickActions":[{"label":"Book an appointment","message":"I would like to book an appointment"}]}}'
Sign in to run this against your account.
JSON
{
  "webchat": {
    "quickActions": [
      { "label": "Book an appointment", "message": "I'd like to book an appointment" },
      { "label": "Opening hours", "message": "What are your opening hours?" }
    ]
  }
}
html
<script src="https://<widget-host>/widget/embed-div.js"
        data-config-id="YOUR_APP_ID"
        data-ghost-chat-lang="auto"></script>
JSON
{
  "webchat": {
    "startLanguage": "auto",
    "welcomeMessages": {
      "Spanish": "¡Hola! ¿En qué puedo ayudarle hoy?",
      "French": "Bonjour ! Comment puis-je vous aider ?"
    }
  }
}
JSON
{ "webchat": { "emergencySignpost": "For a genuine emergency, call 999." } }
  • Assistant-ended chats — when a conversation has naturally concluded (the visitor said goodbye, confirmed they're done, or asked to end), the assistant ends the chat itself. This sends the conversation summary to your team immediately (summary email + PDF transcript to the app's recipients, the same wrap-up voice calls get) and puts the widget into its "chat ended" state, from which the visitor can start a new chat.
  • Visitor-ended chats — the visitor can end at any time via the widget's End chat control (with a confirm step). This runs the same wrap-up immediately.
  • Idle chats — chats left inactive are wrapped up automatically (a sweep runs every ~10 minutes and closes chats idle past ~15 minutes), so a visitor who simply closes the tab still reaches your team.
  • Localised chrome — the widget's own fixed strings (End chat, "This chat has ended…", the confirm prompts, Start a new chat) render in the resolved start language, matching the assistant's conversational language.

Chat doesn't have to be all text. Two mechanisms turn data into native chat UI (cards, tables, timelines, location cards) — in both, the platform owns layout and enforces limits; payloads that don't validate degrade to plain text, never an error.

Declare a render kind on any saved action (tools editor → "Show the reply as", or configuration.render in the stored tool):

Kinds: cards | comparison | timeline | table | metrics | location. The tool's HTTP response body must be the component payload, or carry it under a _render key alongside your other data. Payload shapes match the curated-component shapes below; validation caps counts and lengths, images must be https, and the review-card machinery (editable fields / edit actions) is platform-only and stripped. The "Test this action" panel shows a render verdict (which component would appear, or exactly why it fell back to text). The model's copy of the raw response is capped at 16 KB.

text
"configuration": { "uri": "https://api.you.com/plans", "method": "GET", "render": "cards" }

Two per-action fields turn saved actions into a full conversational controller for your own API — available to any app as pure configuration.

configuration.auth: "visitor-session" — run as the signed-in visitor. Instead of app credentials, the chat forwards the caller's own session token as the request's Authorization header, so your API sees the person and applies its own permissions per call. Enforced server-side at call time:

  • only real signed-in users — anonymous visitors never even see these actions in chat;
  • https URLs only;
  • the URL's origin must be in the app's trusted list (webchat.trustedSessionOrigins, tools editor → "Trusted session origins", up to 8) — the visitor's token is never forwarded anywhere else, and an unlisted origin returns a refusal instead of making the call.

{args.x} path parameters — one action per endpoint, not per record. Tokens in the authored URI path substitute from the action's own arguments at call time: https://api.you.com/v1/orders/{args.orderId} plus a parametersSchema declaring orderId lets the assistant fetch any order. Values are URI-encoded (an argument can never add path separators or change the host), tokens are honoured in the path only, an argument used in the path doesn't also join the GET query string or request body, and a missing argument leaves the token in place so the call 404s loudly rather than hitting the wrong resource. For visitor-session actions the origin check runs on the resolved URL.

Structured (object) parameters — nested request bodies. Some APIs want a nested body (e.g. {"fields": {"Name": "…"}}). Give the action a parameter with type object and describe its fields as a JSON Schema fragment — in the tools editor, pick "Structured (JSON fields)" and paste the fields as JSON, e.g. { "properties": { "Name": { "type": "string" } }, "required": ["Name"] }. The assistant then supplies that parameter as one JSON object inside the request body.

configuration.confirmRequired: true — ask for an explicit yes. The action's generated schema gains a required confirm flag and the assistant is instructed to state exactly what will happen and wait for the user's explicit yes. The hard floor is server-side: without confirm: true the platform refuses and never calls your endpoint. Use it for anything consequential (payments, cancellations, deletions).

Both compose with everything above: credentials stay available for app-authenticated actions, render: shapes the reply, and the webchat.tools allowlist still controls which actions reach the chat at all.

text
"configuration": { "uri": "https://api.you.com/v1/me", "method": "GET", "auth": "visitor-session" }
"webchat": { "trustedSessionOrigins": ["https://api.you.com"] }

Per-app, human-reviewed component payloads stored in voiceChatTools.curatedComponents (up to 8). Each enabled entry becomes a show_<id> tool the assistant may elect, but the content shown is the stored payload verbatim — the model chooses the moment, never the words.

Entry shape (written via the normal app PATCH with voiceChatToolsReplace: true, or managed in the dashboard's Web chat tab):

Kinds and payloads: cards (≤10 items: title, subtitle, badge, facts, sections, actions), table (≤6 columns × 20 rows + footnote), location (name, addressLines, phone, hours, note — the get-directions URL is always server-built from the address), timeline (≤8 steps). These four are the kinds available to curated components; the full render contract for tool results supports six (comparison and metrics are render-directive-only today). Every payload is validated on write AND re-validated at serve time.

Drafting and freshness — the dashboard's Web chat tab can draft a best-guess component set from the app's own knowledge base, and later re-read the knowledge to suggest per-entry updates with reasons. Applying a suggestion is always a human's call; nothing changes without you saving it.

JSON
{ "id": "our_services", "kind": "cards", "name": "Our services",
  "election": "when the visitor asks what services you offer",
  "enabled": true,
  "payload": { "intro": "…", "items": [{ "title": "…", "badge": "£95", "facts": [{"label": "…", "value": "…"}] }] } }