HTML Templates as Calls To Action (Advanced)
HTML Template integrations render custom HTML inside the chat as a rich Call To Action (CTA). Use them for multi‑step wizards, quizzes, guided flows, or custom UI that moves the user to the next step. Templates can work with or without LLM parameters and do not require a data source.
Related: standard button CTAs are covered in CTA Buttons.
How it works
- The assistant decides to call your integration based on its Description text.
- The integration returns an HTML fragment that’s rendered in the conversation.
- Your template can run inline JavaScript to handle events and can call
ai12zBot.sendMessage()
orai12zBot.sendJSON()
to continue the flow.
Quick start: Create an HTML Template CTA
- Go to Integrations → Custom Integrations and click Add Custom Integration.
- In the properties dialog, set when this integration should be used and how it responds:
- Name and describe the scenario so the LLM knows when to call it (e.g., “help pick skis”).
- Data Source:
None
(templates don’t need external data by default). - Handle Response:
Template (HTML)
. - Optional: enable a Custom Function if you’ll post‑process JSON later.
- Add your HTML template and optional parameters, then Save/Publish.
- Test in chat by asking the triggering question. The CTA renders inline and can gather inputs or trigger follow‑ups.
Example: Ski Match quiz
Minimal example showing a small quiz that displays a recommended ski and offers a follow‑up button.
<div style="font-family: Arial, sans-serif; margin: 10px;">
<h3>Find Your Perfect Skis</h3>
<form id="ski-form" autocomplete="off">
<div>
<p><strong>1. What is your skill level?</strong></p>
<label
><input type="radio" name="skill" value="Beginner" required />
Beginner</label
>
<label
><input type="radio" name="skill" value="Intermediate" />
Intermediate</label
>
<label><input type="radio" name="skill" value="Expert" /> Expert</label>
</div>
<div>
<p><strong>2. What terrain do you ski most?</strong></p>
<label
><input type="radio" name="terrain" value="Groomed" required />
Groomed</label
>
<label
><input type="radio" name="terrain" value="Powder" />
Powder/Off‑piste</label
>
<label
><input type="radio" name="terrain" value="Park" /> Terrain Park</label
>
<label
><input type="radio" name="terrain" value="Mixed" /> A bit of
everything</label
>
</div>
<button
type="button"
onclick="handleSkiFormSubmitFromButton(this)"
style="margin-top: 10px; background:#14a3b8; border:0; padding:10px 16px; color:#fff; border-radius:4px; cursor:pointer;"
>
Show My Match
</button>
</form>
<div id="ski-result" style="margin-top: 16px;"></div>
</div>
// Handle quiz submit
function handleSkiFormSubmitFromButton(button) {
const form = button.closest("form")
if (!form) return
const skill = form.querySelector('input[name="skill"]:checked')?.value
const terrain = form.querySelector('input[name="terrain"]:checked')?.value
const match = findSkiMatch(skill, terrain)
const resultDiv = form.parentNode.querySelector("#ski-result")
if (!resultDiv) return
resultDiv.innerHTML = `
<div style="display:flex;gap:12px;align-items:flex-start;border:1px solid #e5e7eb;border-radius:8px;padding:12px;max-width:620px;">
<img src="${match.image}" alt="${match.name}" style="width:160px;height:160px;object-fit:cover;border-radius:6px;" />
<div style="flex:1;">
<h4 style="margin:0 0 6px 0;">${match.name}</h4>
<p style="margin:0 0 12px 0;">${match.type}</p>
<button type="button" class="ai12zBtn" onclick="ai12zBot.sendMessage('Show me more about ${match.name}')">Show more</button>
</div>
</div>`
}
function findSkiMatch(skill, terrain) {
if (skill === "Expert" && terrain === "Powder") {
return {
name: "Faction La Machine 5",
type: "Powder skis",
image:
"https://cdn.shopify.com/s/files/1/0920/9112/1967/files/Faction-Skis-2526-La-Machine-5-Base-1x1.webp?v=1751438691",
}
}
if (terrain === "Park") {
return {
name: "Völkl Revolt 84",
type: "Freestyle skis",
image:
"https://cdn.shopify.com/s/files/1/0920/9112/1967/files/volkl_2425_revolt-84_V2410156_1.webp?v=1751439056",
}
}
return {
name: "Nordica Enforcer 94",
type: "All‑Mountain skis",
image:
"https://cdn.shopify.com/s/files/1/0920/9112/1967/files/700_253125_1081575.webp?v=1751427036",
}
}
Tips & best practices
- Keep templates small and focused; link out for heavy content.
- Prefer semantic HTML and accessible controls (labels, required, keyboard support).
- Use the
ai12zBtn
class for consistent button styling. - Avoid long blocking scripts; update the DOM quickly and call
ai12zBot.sendMessage()
orai12zBot.sendJSON()
for follow‑ups. - If you do use LLM parameters, name them clearly (snake_case) and provide example values.
Troubleshooting
- Nothing renders: ensure Handle Response is set to
Template (HTML)
and the integration is Published. - Clicks don’t work: confirm your inline handlers run in the rendered HTML context; avoid referencing elements outside the template.
- External images/scripts blocked: use HTTPS URLs and ensure your CSP allows them.
- Integration never triggers: adjust the Description so the LLM knows when to call it, and verify the Key Name isn’t conflicting with another tool.