Post-Conversion Triggers¶
Overview¶
Post-conversion qualification is the flow that opens the Rose widget after a visitor submits a client form (e.g., demo request, free trial signup), asking additional profiling questions to enrich the lead before it hits the CRM.
There are three activation modes:
- Thank-you page (
trigger.pages) — the widget activates when the visitor lands on a configured thank-you URL after submitting the client form. - Inline form-submit (
trigger.on_form_submit) — the widget activates on the same page the form lives on, when the client form submits but does not redirect (inline AJAX confirmations like Unbounce, HubSpot embeds, etc.). - Client API (
window.InboundXWidget.startPostConversionQualification) — the client site explicitly starts qualification from its own conversion success handler.
The config-driven modes can be configured simultaneously; whichever condition fires first activates the form. The client API path is for pages where the site has a reliable success signal that Rose cannot observe directly.
IX-2646
The inline form-submit path was introduced for landing pages that show an in-place confirmation instead of redirecting to a thank-you URL. See the Linear ticket for the original use case (Matera).
When to Use Each Mode¶
| Client behaviour after submit | Mode |
|---|---|
| Redirects to a dedicated thank-you page | trigger.pages |
| Shows an inline success message, stays on the same URL | trigger.on_form_submit |
| Conversion success is only known in client code / GTM / third-party callback | startPostConversionQualification() |
| Mixed across pages | Both — each page falls through to whichever matches |
Mode 1 — Thank-You Page (trigger.pages)¶
The default post-conversion path. When the visitor lands on a URL matching one of the configured patterns, SiteConfigManager.getActivePostConversionForm returns the form and the widget auto-opens into qualification.
pagesuses the same wildcard syntax as the widget'sdisplay_only_on_urls(*matches any substring).- No event wiring required — the widget's existing URL-change listener re-evaluates the display decision on navigation.
Mode 2 — Inline Form-Submit (trigger.on_form_submit)¶
When the client form doesn't redirect, Rose listens for the form submission event, persists an activation flag, and dispatches a custom window event so the widget's display controller switches into post-conversion mode on the same page.
{
"trigger": {
"type": "post_conversion",
"pages": [],
"on_form_submit": {
"enabled": true,
"pages": ["/landing/demo*"],
"form_action_matches": ["https://get.example.com/fsg"]
}
}
}
Three safety gates¶
All three must pass before the qualification form activates:
enabled: true— explicit opt-in per form.pagesmatch — current URL matches a configured landing-page pattern.form_action_matchesmatch — the submitted form'sactionattribute matches a configured URL pattern.
The third gate is non-optional: an empty form_action_matches blocks activation even when enabled: true. This is a safety gate — without a per-client form-action contract, any stray submission on the page (newsletter signup, search box, cookie banner) would trigger qualification.
How to fill on_form_submit¶
- Open the landing page in DevTools → Network tab → submit the form with test data.
- Look at the POST request's URL. That's the form action URL — paste it (or a pattern with
*) intoform_action_matches. - Add the landing page path to
pages(the URL where the form lives, not the thank-you URL — there isn't one). - Set
enabled: true.
How it works end-to-end¶
Key invariants¶
prevDecision.allowedmust already betrue. The form-submit event only flips an already-displayed widget into post-conversion mode. It does not force the widget to appear on a page where display rules would otherwise hide it. If you configureon_form_submiton a page that isn't indisplay_only_on_urls/page_display_rules, you'll see a⚠️ DisplayController: form-submit activation ignoredwarning in the console — add the page to the widget's display rules.- Dispatch is gated on a successful
sessionStorage.setItem. If storage is blocked (privacy extensions, ITP in iframes, quota exceeded), the warning is logged and the event is not dispatched, avoiding a silent no-op that would falsely report "activation triggered". - Activation is session-scoped (until tab close).
isFormSubmitActivatedreads fromsessionStorage, so re-opens on revisits within the same tab are intentional — the visitor is nudged back to finish qualification. Two higher-level gates inRoseWidgetprevent unwanted re-prompts:completedPostConversion(localStorage) — skip auto-open if qualification already finished.rose_pcq_dismissed_{domain}(sessionStorage) — skip auto-open if the visitor explicitly dismissed the qualification.
How to verify in production¶
- Submit the form on the landing page with test data.
- Rose widget should open into the qualification form within ~1 second, on the same page.
- In PostHog, confirm
rw_client_form_submittedfires, followed byrw_widget_impressionwithisPostConversion=truein the same session.
Mode 3 — Client API (startPostConversionQualification)¶
When the website has its own reliable success signal, call the widget public API from that success handler:
window.InboundXWidget?.startPostConversionQualification?.({
formId: 'demo-request',
context: {
email: 'lead@example.com',
utm_source: 'google'
},
trackConversion: true
});
formIdselects a specific configured post-conversion form. If omitted, Rose falls back to the first eligible post-conversion form.contextis sent through the normal chat request aspostConversionContextand appears in the completion webhook asvisitor_contact.trackConversiondefaults tofalse; set it only when the API call should also firerw_client_form_submitted.- The activation is session-scoped in
sessionStorage, like inline form-submit activation. - The API path can force the widget display decision on the current page, so it is useful when URL/display-rule matching is the reason automatic activation missed the conversion.
Configuration Reference¶
The JSON schema lives at schemas/configs/website/qualification.schema.json. Generated types:
- TypeScript —
frontend/shared/src/types/configs.generated.ts(QualificationProfilingFormsValueTriggerOnFormSubmit) - Python —
backend/packages/ixconfig/ixconfig/models/generated.py
Related Components¶
| Layer | File |
|---|---|
| Form submission listener | frontend/shared/src/config/PostConversionFormSubmitTracker.ts |
| Display re-evaluation | frontend/widget/src/components/DisplayController.tsx |
| Active form resolution | frontend/shared/src/config/SiteConfigManager.ts (getActivePostConversionForm) |
| Analytics hookup | frontend/shared/src/analytics/index.ts (onFormSubmit → notifyFormSubmit) |
| Widget auto-open gates | frontend/shared/src/components/RoseWidget.tsx |
Related Docs¶
- Qualification Flow & CTA Blocking — pre-conversion qualification behaviour during chat.
- Prospect Qualification (client-facing) — webhook payloads emitted on
post_conversion_complete. - Rose Config —
rose-configskill — operational commands to editqualification.profiling.forms.*.triggerin each environment.