Form replay architecture
When a visitor fills out a contact form inside the MimicBot chat widget, MimicBot needs to deliver that message to the same place a normal form submission would go — the site's backend, the same CRM webhook, the same email inbox. Form replay is the layered system that makes that happen without requiring you to wire up a new integration.
The widget tries three strategies in order. Every attempt, successful or not, is recorded so a visitor message never disappears into the void.
Layer 1: Same-page
If the form that matches the action already exists on the page the visitor is looking at — say, the visitor is on your contact page and asks the bot to "send this for me" — the widget fills the real form in place and calls form.requestSubmit(). The site's own submit handler runs exactly as if a human clicked the button. No iframe, no new tab, no compromises. This is the fastest and most reliable path and should succeed on any correctly-configured site.
Layer 2: Hidden iframe replay
When the form lives on a different page (for example, the visitor is reading a blog post and fills out a contact form inside the chat), the widget mounts a hidden iframe pointing at the form's original page URL. Because the widget runs on your own domain, the iframe loads same-origin — it inherits cookies, CSRF tokens, and any client-side React submit handlers the page wires up at load time.
Inside the iframe, the widget:
- Fills each mapped field using controlled-input setters so React state actually updates
- Calls
form.requestSubmit()to trigger the site's own onSubmit handler - Races four success signals: a network 2xx response, a DOM mutation typical of a success state, a navigation event, or a 5-second optimistic timeout
If any signal fires, the execution is marked delivered and the visitor sees confirmation inside the chat. The real form submission already went to your backend.
Layer 3: Deep-link fallback
Some sites block iframe-based replay outright — an X-Frame-Options header, a strict CSP, a reCAPTCHA challenge, an ad blocker intercepting the iframe. When layer 2 fails, the widget opens the form's page in a new tab with the visitor's field values appended as query parameters (?name=...&email=...&message=...) and a short prompt asks the visitor to review and click Send. The visitor stays in control, the form is prefilled, and the submission still goes to your backend — it just takes one extra click.
This layer works on any framework and any hosting setup. It's the universal fallback.
Safety net
Every form submission, no matter which layer handled it, writes a row to action_executions with a contactMetadata JSONB payload that includes deliveryStatus, deliveryMethod, signalSource, and timing fields. The dashboard's Contact Submissions view is a filtered read of this table.
This means two things in practice:
- You can audit every contact form submission the bot ever touched, ordered by time, with the visitor's values intact.
- A submission that the widget couldn't confirm delivered (layer 2 timed out, layer 3 was never clicked) still leaves a full record so you can reach the visitor manually.
Visitor messages do not disappear.
Debugging a failing submission
If a contact form execution shows up as failed or never-confirmed, walk the following checklist:
- Open the execution in the dashboard's Contact Submissions view. A
signalSourceoftimeoutmeans the widget submitted the form but couldn't detect a success signal — the submission probably went through; check your inbox or CRM. - Inspect the action's
target.selector,target.fieldMap, andtarget.hasCaptcha. A stale selector after a site redesign is the most common cause of field-mapping mismatches. - Re-crawl the bot from the dashboard. The crawler's upsert logic refreshes the recipe in place, so a re-crawl is enough after the site changes.
- If the selector and field map look correct but submission still fails, check whether a captcha or ad blocker is intercepting the iframe. Layer 3 (deep-link) is your fallback here, and the visitor experience is still usable.
Known limitations
- reCAPTCHA, hCaptcha, Turnstile — detected at crawl time and runtime. The widget skips the iframe layer and goes straight to deep-link.
X-Frame-Options: DENYor CSPframe-src 'none'— the iframe fails to load; the widget falls through to deep-link.- Authenticated contact forms — the iframe loads the login wall instead of the form; the widget falls through to deep-link.
- Aggressive ad blockers — may block the hidden iframe entirely; detected via exception handling and followed by a deep-link fallback.
In each of these cases, delivery still happens — just via the deep-link layer with one extra visitor click.