Concepts

Customize the flow

Branding, theme, locale, prefill and copy overrides for the hosted verification page.

The hosted flow is white-labelled: it renders with your logo, colours, copy and theme so the applicant sees your brand, not ThirdFactor's. Most styling is a tenant/workflow-level property set in the console — it applies consistently across every channel you send an applicant through. Only a small set of knobs (locale, prefill) is set per session at create time.

What is set where

AspectWhere it's configuredPer session?
Logo, colour, button shape, themeConsole Config → KYC Workflows → Design panel, and tenant defaults in KYC settingsNo — tenant/workflow-level
Applicant-facing copy (per-locale overrides)Console Settings → TranslationsNo — tenant-level
Terms & ConditionsConsole Settings → TermsNo — tenant-level
UI languagelocale on POST /v3/session/Yes
Pre-populated identity fieldsprefill on POST /v3/session/Yes

Note

There is no per-session branding override on the API. Colour, logo and theme are tenant/workflow properties by design — that consistency is what keeps the applicant's experience the same whether they arrive via a link, an SDK, or a console-generated URL.

Branding & theme

The Design panel in the workflow builder controls the look of the hosted page. Workflow-level values override tenant defaults, so a designer sees exactly what they styled. The customization blob the flow consumes supports:

KeyTypeEffect
primary_color / colorstringAccent colour for buttons and highlights.
logo_urlstringBrand logo shown in the flow header (falls back to the tenant's client_logo_url).
button_radiussm | md | fullCorner rounding of primary buttons.
themelight | darkForces the flow's colour scheme.
languageen | neDefault flow language.
welcome_title / welcome_message / button_textstringIntro-screen copy (session vocabulary).
headline / body / cta_textstringIntro-screen copy (Design-panel vocabulary).
allow_uploadbooleanAllow file upload for document capture (default true).
allow_desktopbooleanAllow completing on desktop (default true); when desktop is allowed you can also show a "continue on your phone" QR.

Put together, that's the shape the flow reads at render time — an operator never edits this JSON directly (the Design panel writes it), but it's useful to see the whole object when you're debugging why something isn't rendering as expected:

{
  "primary_color": "#0F62FE",
  "logo_url": "https://cdn.acme.example.com/logo.svg",
  "button_radius": "md",
  "theme": "dark",
  "language": "en",
  "welcome_title": "Verify your identity",
  "welcome_message": "This takes about 3 minutes. Have your ID ready.",
  "button_text": "Start verification",
  "allow_upload": true,
  "allow_desktop": true
}

Info

These keys are operator-authored in the console, not accepted on the create-session API. The API-created session inherits whatever the tenant and its resolved workflow have configured.

Warning

Common pitfall. Setting logo_url or primary_color at the tenant level and expecting it to apply everywhere, then being surprised a specific workflow still shows the old look — workflow-level Design values override tenant defaults. If a workflow was styled before you updated the tenant default, that workflow keeps its own values until someone re-saves its Design panel.

Dark mode

Set theme to dark (or light) in the Design panel to force a colour scheme. The hosted flow resolves the theme server-side from the session token before the first frame, so a dark flow paints dark immediately — there is no light flash before hydration on the loading and gate screens.

Locale

locale on POST /v3/session/ sets the UI language for that specific session (default en):

Request

{
  "workflow_id": "9c2f1c4e-9a2b-4f2f-8f0f-0f1f3f4f5f6f",
  "locale": "ne"
}

Responselocale itself isn't echoed back on the session object, but it takes effect immediately on the hosted page:

{
  "id": "6f0e2b7a-…",
  "session_kind": "user",
  "status": "not_started",
  "url": "https://acme.example.com/verify/<session_token>",
  "vendor_data": null,
  "decision": "in_progress",
  "expires_at": "2026-07-13T08:00:00Z",
  "created_at": "2026-07-12T08:00:00Z"
}

The flow ships English (en) and Nepali (ne) copy. Per-locale text can be overridden tenant-wide (see below), so locale selects which override set the applicant sees.

Note

Sending an unsupported locale value doesn't error the create call — the flow falls back to its default (en) copy for anything it doesn't have a translation set for. If an applicant reports seeing English when you expected Nepali, check the exact locale string you sent before assuming a bug.

Copy overrides (translations)

Under Settings → Translations an operator can override any applicant-facing string per locale — for example the intro title, step instructions, or button labels. Overrides are keyed by the flow's i18n dictionary and validated against a curated catalog, so only real, applicant-safe strings can be set. They are merged into the flow at render time and honour the session locale.

{
  "en": { "kyc.intro.title": "Verify your identity with Acme Bank" },
  "ne": { "kyc.intro.title": "एक्मे बैंकसँग आफ्नो पहिचान प्रमाणित गर्नुहोस्" }
}

Tip

Because overrides are validated against a curated key catalog, you can't accidentally inject markup or break the flow's layout — an unknown key is simply not applied. If your override doesn't show up, double-check the exact dictionary key rather than the text itself.

Terms & Conditions

Under Settings → Terms you can attach versioned Terms & Conditions to the flow's start screen, optionally requiring explicit acceptance before the applicant can proceed. T&C carry a version and per-locale title / body; editing the content bumps the version and pushes the superseded copy onto its history, and an acceptance record is captured against the session.

Warning

Common pitfall. If T&C acceptance is required but a locale is missing title/body copy, applicants in that locale can land on a start screen with fallback or empty text and hesitate to accept — which looks identical to "the applicant is stuck on a step." Before shipping a new locale, confirm both Settings → Translations and Settings → Terms have copy for it, not just one.

Prefill

prefill on POST /v3/session/ pre-populates identity fields so the applicant types less. Only a fixed set of keys is read (unknown keys are ignored):

{
  "prefill": {
    "full_name": "Aarav Sharma",
    "first_name": "Aarav",
    "last_name": "Sharma",
    "display_name": "Aarav",
    "date_of_birth": "1990-04-12",
    "email": "[email protected]",
    "phone": "9841000000",
    "nationality": "NP",
    "address_line1": "House 12, Baneshwor",
    "address_line2": "Ward 10",
    "city": "Kathmandu",
    "state": "Bagmati",
    "country": "NP",
    "zip_code": "44600"
  }
}

See Create a session for the full field reference.

Warning

Prefill is a convenience, not a source of truth. The workflow still verifies the applicant's real document and biometrics — prefilled values don't shortcut any module or influence the decision.

Hosted flow vs. SDK customization

Both delivery paths render the same Design-panel branding — the difference is what else you can control from the host application:

Hosted flow (redirect)SDK (embedded)
Logo, colour, theme, button shape✅ tenant/workflow Design panel✅ same Design panel
Copy per locale✅ Settings → Translations✅ same, via locale
Container styling (size, position, host page around it)❌ full-page, you don't control the chrome✅ your app controls the embedding container
Light/dark resolved before first paint✅ server-side from the session token✅ same
Per-session prefillprefill at create time✅ same field, same endpoint

Troubleshooting

Warning

Applicant stuck on a step because of a customization gap. The most common cause isn't a broken module — it's missing copy for the locale the applicant is actually using. Check, in order: (1) does Settings → Translations have an entry for locale, (2) does Settings → Terms have T&C copy for that same locale if acceptance is required, (3) is theme forcing a colour scheme that makes text unreadable against a custom logo_url. Pull GET /v3/session/<id>/logs/ to see the exact step the applicant stalled on.

Note

Branding changes made in the console Design panel apply to new sessions created after the save — a session already created (and its url already handed to an applicant) keeps rendering with whatever was configured when the flow first resolved the token. If a design change "isn't showing up," confirm you're testing with a freshly created session, not a stale link from before the change.

FAQ