{ email }
{{ $json.email }}
{{ $json.resetCode }}
Hey Andrei,
You're reset code is olbBro
Set up a new Voiceflow agent (or use an existing one). In the canvas:
ext_resetFormtrace.addTrace() to emit the trace that the form extension will match oncomplete interaction from the extensionIn your n8n instance, create a new workflow with 3 nodes:
POST, path to /webhook/reset-code. This receives { email } from the form extension.{{ $json.email }}, subject to "Reset Code", and body to include {{ $json.resetCode }}.Activate the workflow and copy the webhook production URL.
In Bolt.new, create a new project with an index.html file:
ResetFormExtension object with type: 'response', match, and render functionsrender function, create an email input form and handle submission with a fetch() POST to your n8n webhook URLwindow.voiceflow.chat.interact({ type: 'complete', payload: { email } })assistant.extensionsRun the Bolt.new preview and interact with the Voiceflow chat widget:
The ResetFormExtension is a custom response-type extension registered in the Voiceflow chat widget configuration. It works by:
ext_resetForm) emitted by the Voiceflow Function stepelement DOM nodefetch() POST request to the n8n webhook with the email addresswindow.voiceflow.chat.interact() to continue the conversation flowThe n8n workflow is a 3-node linear pipeline:
/webhook/reset-code. Receives { email: "user@example.com" } from the form extension's fetch() call.{ email, resetCode } to the next node.{{ $json.email }}, subject is "Reset Code", and the body contains the generated code.The entire frontend — including the Voiceflow widget script, the custom extension definition, and any styling — is hosted as a static HTML page in Bolt.new. Key points:
index.html alongside the Voiceflow widget loaderThe puzzle clue says: "Visit the Voiceflow Docs on how to create custom extensions." The Voiceflow documentation describes two types of extensions:
match function to detect custom traces and a render function to build the DOM.For this puzzle, we use a response extension because we need to render an interactive email form directly inside the chat message area. The key insight from the docs is the match → render → interact lifecycle.