Docs On your website

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

  1. 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.

  2. 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.

  3. 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 header Authorization: Bearer <token>.

  4. 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 Authorization header. An Authorization header 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; and proxy_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 Cookie header and let Set-Cookie come 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-src and img-src as in The widget on your website and put your own domain ('self') in connect-src instead of your subdomain.

  • Website tools: if your agent uses website tools, pass the browser's Origin and Referer headers 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.

Still have a question?

Ask it here. The answer comes from this documentation and points to the right page.

What exactly is vragen.ai?

Example answer by vragen.ai

vragen.ai lets visitors ask their question on your website and gives them a reliable answer straight from your own content, with the source included. You decide which sources the AI uses.

Source: How it works

This is an example. The interactive widget could not load here, for instance because of a script blocker or a slow connection.

You are talking to an AI assistant from vragen.ai. Why we mention this