A traditional FAQ has a predictable failure mode. It answers the four questions you chose, then leaves everyone else to search another page or open a separate support channel.
An infinite FAQ keeps the useful, scannable part and removes that dead end. Show a small set of trusted answers first. Put one question field underneath. Let your Agentkit agent answer everything else on the page. If the visitor needs more help, open a full chat with the question and answer already in its history.
That last detail matters. Sending the question to one API and opening a separate chatbot can produce two disconnected conversations. The visitor sees repetition, while your support team sees split context. Agentkit’s embed SDK avoids that problem: send adds a message to the Center Stage conversation without opening the dialog, and open reveals that same conversation later.
This guide builds the pattern we use on the Agentkit homepage.
What You Are Building
The interaction has three layers:
- Four normal FAQ rows provide immediate, crawlable answers.
- A compact question form sends a message while Center Stage stays closed.
- The arrow opens that same conversation in Center Stage with its context intact.
The fixed answers should remain ordinary HTML. They load instantly, work without JavaScript, and can be read by search engines and assistive technology. AI handles the long tail rather than replacing content you already know visitors need.
1. Prepare the Agent
Create an agent and add the sources it should use. For the predictable questions, add explicit Q&A sources so the canonical wording wins. Add your website or documentation for everything else.
Before embedding anything, test questions that are:
- answered directly by one Q&A pair;
- answered across several documentation pages;
- outside the agent’s intended scope;
- phrased differently from your source heading.
The inline answer is only as reliable as the agent behind it. The interface should not disguise a weak source set.
2. Enable Center Stage
Open the agent’s Publish settings and enable Center Stage. Configure its title, welcome text, instructions, allowed actions, security settings, and rate limit for this channel.
Center Stage uses a focused dialog rather than a corner bubble. It is a good continuation surface here because the visitor has already moved from browsing fixed answers into a specific support question.
Only mount one Agentkit presentation on a page. If the page uses Center Stage, do not also load a second bubble embed.
3. Install the Embed and Command Queue
Copy the embed snippet from the Publish page. The queue bootstrap makes SDK calls safe even when the async widget bundle has not loaded yet.
<script>
(function (w) {
if (w.agentkit) return;
var q = [];
var agentkit = function () { q.push(arguments); };
agentkit.q = q;
w.agentkit = agentkit;
})(window);
</script>
<script
async
data-chatbot="YOUR_AGENT_ID"
data-display="center-stage"
src="https://agentkit.ai/dist/agentkit-chatbot.js">
</script>
Use the widget host shown in your own Publish page if it differs from agentkit.ai.
4. Add the FAQ and Answer Region
The fixed questions can use native details elements. The answer region uses aria-live so a screen reader announces the reply when it arrives.
<section aria-labelledby="faq-heading">
<h2 id="faq-heading">Questions</h2>
<p>Start with the short answers. Ask anything else below.</p>
<details>
<summary>What does your product do?</summary>
<p>Your canonical short answer goes here.</p>
</details>
<!-- Add three more high-volume questions. -->
<form id="ai-faq-form">
<label for="ai-faq-question">Ask anything else</label>
<input id="ai-faq-question" maxlength="500" required>
<button id="open-chat" type="button" aria-label="Continue in Center Stage">→</button>
</form>
<div id="ai-faq-answer" aria-live="polite">
<p id="ai-faq-finding" role="status" hidden>Finding the answer…</p>
<p id="ai-faq-response"></p>
</div>
</section>
Keep the form small. It is an escape hatch from the FAQ, not a second full chat composer.
5. Answer After a Pause, Then Continue
Register both message listeners before sending. The user-message event confirms that the widget accepted this question. Only then should the next assistant-message event supply the completed answer for the page. This prevents an unrelated assistant event from being shown if the send is refused.
Debounce the input so the inline answer starts after the visitor stops typing. Return can generate the answer immediately, while the arrow is reserved for the explicit move into Center Stage. A lightweight pulsing status can occupy the answer region while the response is being generated without replacing the arrow with a spinner.
<script>
const form = document.querySelector('#ai-faq-form');
const input = document.querySelector('#ai-faq-question');
const openButton = document.querySelector('#open-chat');
const finding = document.querySelector('#ai-faq-finding');
const response = document.querySelector('#ai-faq-response');
let sendTimer = null;
let pendingQuestion = null;
let submittedQuestion = null;
let questionAccepted = false;
function onUserMessage(event) {
if (event.detail.content === pendingQuestion) {
questionAccepted = true;
}
}
function onAssistantMessage(event) {
if (!questionAccepted) return;
const answered = pendingQuestion;
finding.hidden = true;
pendingQuestion = null;
questionAccepted = false;
// Show the answer only while the field still holds the question it
// belongs to; otherwise ask the edited question now that the widget is free.
if (input.value.trim() === answered) {
response.textContent = event.detail.content;
} else {
scheduleAnswer();
}
}
window.agentkit('addEventListener', 'user-message', onUserMessage);
window.agentkit(
'addEventListener',
'assistant-message',
onAssistantMessage,
);
function sendAnswer(message) {
if (!message || pendingQuestion || message === submittedQuestion) return;
finding.hidden = false;
pendingQuestion = message;
submittedQuestion = message;
questionAccepted = false;
window.agentkit('send', { message });
}
function scheduleAnswer() {
window.clearTimeout(sendTimer);
const message = input.value.trim();
sendTimer = window.setTimeout(() => sendAnswer(message), 800);
}
input.addEventListener('input', function () {
response.textContent = '';
scheduleAnswer();
});
form.addEventListener('submit', function (event) {
event.preventDefault();
window.clearTimeout(sendTimer);
sendAnswer(input.value.trim());
});
openButton.addEventListener('click', function () {
window.agentkit('open');
});
</script>
Do not send the question again when opening. open() reveals the existing conversation, so the visitor sees the question and response that appeared inline and can type the next message immediately.
The SDK applies the same composer checks and rate limits to send as it does to messages sent inside the dialog. Empty messages, concurrent sends, disabled input, and live-chat takeovers are refused.
Production Details Worth Keeping
Add a visible timeout or retry state. A page-level form does not automatically show the widget’s internal error UI while the presentation is closed. We use a bounded wait and let the visitor retry if no assistant event arrives.
Keep the submitted question in your component state until the answer returns. Clear an old answer as soon as the visitor edits the question, so the page never pairs stale output with new input.
Remove event listeners when a client-rendered page unmounts. Pass the same function reference to removeEventListener:
window.agentkit( 'removeEventListener', 'user-message', onUserMessage, ); window.agentkit( 'removeEventListener', 'assistant-message', onAssistantMessage, );
Finally, test the complete flow on a phone. Center Stage fills the small viewport, traps focus, closes with its close control, and returns focus to the arrow that opened it. Your inline form still needs its own readable label, visible pending state, and live answer announcement.
When to Use the REST API Instead
The embed SDK is the right API for this browser interaction because it owns the conversation that Center Stage later opens. Use API v2 for server-to-server chat, a completely custom chat interface, conversation exports, source management, or integrations that should never load the Agentkit widget.
API v2 uses a workspace bearer key. Keep that key on your server and never place it in page JavaScript. A server-rendered FAQ could call POST /api/v2/agents/{agentId}/chat, but its API conversation is intentionally separate from a Center Stage web conversation. If seamless browser handoff is the goal, use send and open through the embed SDK.
The Design Rule
Use static answers for what you know people ask. Use the agent for what you did not predict. Preserve one conversation when the visitor moves from the page into chat.
That combination makes the FAQ faster for most visitors, more useful for everyone else, and easier for your team to review as one coherent support interaction.


