Deploying your assistant
The Deploy tab is where you decide how your assistant proves it is allowed to run, copy the embed code for your website, and test the result before anyone else sees it.
Open an assistant from Assistants, then select the Deploy tab.

Authentication Mode
Every request the widget makes has to be authenticated, or anyone could copy your embed code onto their own site and spend your credits. Authentication Mode picks how that happens. Two modes are available, shown as a pair of cards:
| Mode | What it does | Use it when |
|---|---|---|
| Allowed Websites | The widget sends only your site ID. The server checks the browser's Origin header against your list of allowed domains. No secrets in the browser. |
Public websites — this is the default. |
| Session Signed | Your backend mints a short-lived signed session token for each visitor and hands it to the page. Tokens can carry a user identity. | Apps that already have their own login and want the assistant to know who the visitor is. |
Session Signed carries a PAID badge and cannot be selected until it is unlocked on your workspace; selecting it before then shows the message "Session Signed auth is a paid feature. Add credits to unlock it."
The rest of the tab changes with the mode you pick: Allowed Websites shows the domain list and the script tag, Session Signed shows the backend integration code and the Server Secret panel.
Allowed Domains
In Allowed Websites mode, the Allowed Domains panel lists every domain the assistant is allowed to load on. Requests from anywhere else are rejected.
- Type a domain into the field (for example
example.comorlocalhost:5566) and press Enter or click Add Domain. - The value is cleaned up as it is saved:
https://orhttp://is stripped, anything after the first/is dropped, and the result is lowercased. Pastinghttps://example.com/pricingstoresexample.com. - Each saved domain appears as a chip. Click its × to remove it.
- Changes are saved with the rest of the assistant — use Save when you are done.
Matching is exact — there are no subdomain wildcards
The origin's host must equal an entry in the list. example.com does not cover
www.example.com, shop.example.com, or example.co.uk. Add every host you embed on as its
own entry.
Ports follow one rule: an entry that includes a port (localhost:5566) matches that host and
port exactly, while an entry without a port matches the host on any port.
If the list is empty the panel shows "No domains configured. Add at least one domain." and the widget will not authenticate anywhere — the server rejects the request with "Site has no allowed domains configured." A request from a host that isn't on the list is rejected with "Origin not in allowed domains."
Embed Code
The Embed Code section gives you a ready-made script tag with your site ID already filled in.
Copy it with the button on the code block and paste it into your site's HTML, just before the
closing </body> tag:
<script src="https://hiroi.ai/static/va-wave-widget.js"
data-site-id="YOUR-SITE-ID"></script>
The site ID is safe to publish. It is not a secret — the server validates the Origin header
against your Allowed Domains on every request, which is what actually protects you.
Appearance and behaviour come from the settings you saved in the dashboard, so the tag stays this
short. Optional data- attributes can override individual settings per page — see
Embedding the widget.
Platform guides
The Platform Guides accordion under the code block has step-by-step instructions for the four hosted platforms people ask about most. In every case, paste the script tag from above and then add that platform's domain to Allowed Domains.
Squarespace
- In your Squarespace dashboard, go to Settings → Advanced → Code Injection.
- Paste the script tag into the Footer field.
- Click Save.
- Add your Squarespace domain (for example
yoursite.squarespace.com) to Allowed Domains.
WordPress
- Go to Appearance → Theme Editor, or use a plugin such as Insert Headers and Footers.
- Open your theme's
footer.phpfile. - Paste the script tag just before the closing
</body>tag. - Add your WordPress domain to Allowed Domains.
The WPCode plugin is the easiest way to add scripts without editing theme files.
Wix
- In your Wix dashboard, go to Settings → Custom Code (under Advanced).
- Click Add Custom Code.
- Paste the script tag, set placement to Body - End, and apply to All Pages.
- Click Apply.
- Add your Wix domain to Allowed Domains.
Shopify
- In your Shopify admin, go to Online Store → Themes.
- Click Actions → Edit Code on your active theme.
- Open
theme.liquid. - Paste the script tag just before the closing
</body>tag. - Add your Shopify domain to Allowed Domains.
Quick test
Test Widget opens a test page with your assistant already loaded, in a new tab. Nothing has to be embedded anywhere for this to work, so it is the fastest way to hear a voice or try a few questions.
The test page shows the last saved version of the assistant. If you have unsaved edits the tab says so — save first, then test.
Server Secret
The Server Secret section appears in Session Signed mode only. The secret is how your backend proves to Hiroi that it is allowed to mint session tokens, so it lives on your server and never in the browser.
Click Generate Server Secret. The secret is shown once, in full, with a copy button and the warning "Copy this now — it won't be shown again." Store it wherever you keep your other server credentials. Hiroi keeps only a hash of it plus the last six characters, which is what the panel shows on later visits.
Secrets start with ss_.
Regenerating invalidates the old secret immediately
Once a secret exists the button becomes Regenerate Server Secret and asks you to confirm. The moment you confirm, the old secret stops working — any backend still sending it will start failing. Deploy the new secret everywhere before, or immediately after, you regenerate.
The server-side flow
With Session Signed selected, the tab shows the shape of the integration: your backend calls the session endpoint with the secret, gets a short-lived token back, and passes that token to the page.
// 1. On your backend — never in browser code
const response = await fetch('https://hiroi.ai/api/widget/session/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Server-Key': 'YOUR_SERVER_SECRET' // ss_...
},
body: JSON.stringify({
user_id: 'user123', // optional: identify the visitor
ttl_minutes: 15 // optional: 1–60, default 15
})
});
const { session_token } = await response.json();
<!-- 2. Render the token into the page -->
<script src="https://hiroi.ai/static/va-wave-widget.js"
data-session-token="THE_TOKEN_FROM_STEP_1"></script>
Tokens are short-lived by design — the endpoint accepts ttl_minutes between 1 and 60 and defaults
to 15 — so mint a fresh one per page load rather than caching it.
Full request and response details, including the optional user fields, are in Widget authentication.
Troubleshooting
The widget doesn't appear at all. Check that the script's src really points at
/static/va-wave-widget.js. The bundle only starts up when it finds its own script tag on the page,
so a renamed or proxied copy will load and then do nothing.
The widget appears but every message fails. You are almost certainly in Allowed Websites
mode on a host that isn't in the list — including the www. variant of a host you did add, or a
staging subdomain. Add the exact host and save.
Session Signed pages fail after a while. The session token expired. Mint a new one on each page load instead of reusing one.