The widget in a locked-down environment
By default the widget talks directly to your own vragen.ai subdomain, without credentials: every visitor may ask a question, and the brake against abuse sits on our side. Some organizations want it differently. The API of their environment may only be reachable with a token, so nobody can ask questions or search outside their own website. That works. You lock the environment and route the widget through a proxy on your own domain. The proxy adds the token, and the token never reaches the browser.
How it fits together
-
In your environment, set Widget to "Only signed-in users and API tokens of this account". You find it under Settings, Security settings tab, in the Access section (see the dashboard); only an Owner can open that tab. From then on, your subdomain refuses every widget call without a valid token. Colleagues who are signed in to vragen.ai can keep using the widget. The widget's script and styling still load as before; there is no knowledge in them.
-
Create a token for the proxy, under Integrations with the API connections. Any API token of your environment gives access to the widget, whatever other rights it has. So give the token as few rights as possible; it lives on your server only.
-
Set up a proxy on your own domain, for example
https://www.yoursite.com/vragen-proxy/, that forwards the widget's calls one to one to your vragen.ai subdomain and adds the headerAuthorization: Bearer <token>. -
In the embed script, pass the proxy address per call (see below). The widget then only talks to your own domain.
Your CMS integrations do not notice a thing. The WordPress plugin, the Drupal module and your own API integration already talk server-side with a token.
Which calls the widget makes
All calls go to your subdomain and carry your environment's identifier in the path ([uuid] here). For each call, the table shows which parameter on the embed script sends it to your proxy.
| Call | What for | Parameter on the embed script |
|---|---|---|
GET /agent/[uuid]/start |
An agent's answer, as a stream (Server-Sent Events) | agentEndpoint |
GET /agent/[uuid]/resume |
Continuing an agent conversation after an intermediate step | agentResumeEndpoint |
GET /agent/[uuid]/continue |
Continuing an agent's answer after the widget has sent back the outcome of a website tool (website tools only) | agentContinueEndpoint |
POST /agent/[uuid]/webmcp-tools |
The website tools the page offers (website tools only) | webmcpToolsEndpoint |
POST /agent/[uuid]/tool-results |
The outcome of a website tool (website tools only) | toolResultsEndpoint |
GET /conversation/[uuid]/state |
Preparing a conversation; the widget calls this just before the visitor asks a question | stateEndpoint |
GET /conversation/[uuid]/start |
The answer of an answer system without an agent, as a stream | answerEndpoint |
POST /conversation/[uuid]/cancel |
The visitor stopping an answer | cancelEndpoint |
GET /conversation/[uuid]/results |
The search results for a question | searchEndpoint |
GET /conversation/[uuid]/questions |
Question suggestions | questionSuggestionEndpoint |
GET /conversation/[uuid]/autocomplete |
Autocomplete in your search field (also on embed-autocomplete.js) |
autocompleteEndpoint |
GET /conversation/[uuid]/facets |
The facets for the filter panel | filterFacetsEndpoint |
POST /conversation/[uuid]/feedback |
A visitor's thumbs up or down, with a comment | feedbackEndpoint |
The exact addresses, including the parameters your deployment sometimes appends (such as the selected agent or search strategy), can be read from the script itself. Open https://yourorganization.vragen.ai/embed.js?deployment=[deployment-name] in your browser and look for _endpoint in the config block. Copy those addresses verbatim and replace only the domain with your proxy.
Which calls your deployment really makes depends on what is switched on. Preparing a conversation belongs to every deployment where visitors ask a question. A deployment with an agent uses the agent routes and stopping. The three website tool calls are only needed when your agent uses Website tools (WebMCP): actions your own website offers the agent, such as booking an appointment (see Agents and agent mode). Beyond that, without a filter panel there are no facets and without a search field no autocomplete. Pass the parameters that belong to your deployment and leave out the rest.
The embed script
You pass the proxy addresses as extra parameters on the script URL. They are full addresses, so encode them as URL values (in JavaScript: encodeURIComponent), otherwise the ? and & inside them break the parameter list. Readable, before encoding:
https://yourorganization.vragen.ai/embed.js
?deployment=[deployment-name]
&agentEndpoint=https://www.yoursite.com/vragen-proxy/agent/[uuid]/start?agent=[agent-uuid]
&agentResumeEndpoint=https://www.yoursite.com/vragen-proxy/agent/[uuid]/resume
&stateEndpoint=https://www.yoursite.com/vragen-proxy/conversation/[uuid]/state
&cancelEndpoint=https://www.yoursite.com/vragen-proxy/conversation/[uuid]/cancel
&searchEndpoint=https://www.yoursite.com/vragen-proxy/conversation/[uuid]/results
&questionSuggestionEndpoint=https://www.yoursite.com/vragen-proxy/conversation/[uuid]/questions
&feedbackEndpoint=https://www.yoursite.com/vragen-proxy/conversation/[uuid]/feedback
Encoded, that becomes one line, which you place on your page as an ordinary script tag:
<script src="https://yourorganization.vragen.ai/embed.js?deployment=[deployment-name]&agentEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fagent%2F[uuid]%2Fstart%3Fagent%3D[agent-uuid]&agentResumeEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fagent%2F[uuid]%2Fresume&stateEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fconversation%2F[uuid]%2Fstate&cancelEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fconversation%2F[uuid]%2Fcancel&searchEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fconversation%2F[uuid]%2Fresults&questionSuggestionEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fconversation%2F[uuid]%2Fquestions&feedbackEndpoint=https%3A%2F%2Fwww.yoursite.com%2Fvragen-proxy%2Fconversation%2F[uuid]%2Ffeedback"></script>
Leave a parameter out and the widget uses the regular address on your subdomain for that call, which fails as soon as the environment is locked. After placing the script, check in the browser console (F12, Network tab) that no call goes directly to your subdomain anymore, apart from the script and the styling.
What your proxy needs to get right
-
Forward as received: method, path, query string, body and
Content-Type. Search and suggestions are plain GET requests that expect JSON; feedback is a POST with JSON; stopping an answer is a POST with form fields, which the browser also sends when the tab is closed. -
Add only the
Authorizationheader. AnAuthorizationheader coming from the browser is not passed on. -
Allow only the paths above, with your environment's fixed
[uuid]. Put your whole subdomain behind the proxy and your token becomes an open relay to the complete API of your environment, knowledge base included. -
Streaming: the answer arrives as Server-Sent Events over one connection that stays open until the answer is done, sometimes a minute. Disable buffering and allow long connections; in nginx, for example,
proxy_buffering off;,proxy_http_version 1.1;andproxy_read_timeout 300s;. If your proxy buffers anyway, the visitor sees nothing until the answer is complete. -
Cookies: the widget sends cookies so that follow-up questions and visitor statistics belong to the same visitor. Forward the
Cookieheader and letSet-Cookiecome back, with your proxy's domain. Skip that and the widget still works, but every question counts as a new visitor in your conversations. -
One sender: to us, all your visitors now arrive from your proxy's address. Let us know at service@vragen.ai and we set the brake against abuse per environment instead of per visitor (see Answer limit and fair use). Otherwise the proxy hits a brake meant for a single visitor on a busy day.
-
Content Security Policy: script, styling and images still come from your subdomain, while the connections now go to your own domain. Keep
script-src,style-srcandimg-srcas in The widget on your website and put your own domain ('self') inconnect-srcinstead of your subdomain. -
Website tools: if your agent uses website tools, pass the browser's
OriginandRefererheaders on unchanged. vragen.ai only offers a page's actions to the agent when the request comes from one of that agent's "Trusted websites". Lose that origin and the widget keeps working, but the agent does not use your site's actions. -
Same domain: put the proxy on the domain of the page that hosts the widget. Then CORS does not come into play and cookies keep working by themselves.
What you gain
Nobody can ask your environment questions from outside your website, the token never leaves your server, and all widget traffic passes through a point you control: your own logging, your own firewall, your own rules. The visitor notices nothing; they see the same widget with the same answers.
Support
Stuck, or want us to look over your proxy configuration? Email service@vragen.ai.