Server-Side Proxy

The most secure integration method. Your server fetches the widget HTML from Returning.AI, injects the JWT token server-side, and serves it to the browser. The API key never touches the client.

Architecture

Every request flows through your backend. The browser never communicates directly with the Returning.AI API.

Browser  ->  Your Server  ->  Returning.AI API
                  |
           1. POST /widget/{id}/signin   (get JWT)
           2. GET /{widget-type}/{widgetId}  (get HTML)
           3. Inject token into HTML
                  |
Browser  <-  Modified HTML with embedded token

Security model

A clear separation between server-side secrets and browser-visible data.

Stays on your serverGoes to the browser
WIDGET_API_KEYJWT (short-lived, user-scoped)
WIDGET_COMMUNITY_IDWidget ID
Returning.AI API requestsRendered widget HTML (with token embedded)

When to use

  • Maximum security requirements - API key never leaves the server.
  • Full control over the widget lifecycle (caching, error pages, fallbacks).
  • Domain whitelisting is insufficient for your security model.

Getting started

Five steps from zero to a working server-side proxy.

1Store credentials server-side

Keep your API key and community ID in environment variables. They must never be included in client-side code or HTML responses.

.env
WIDGET_BASE_URL=https://prod-widgets.returning.ai
WIDGET_API_KEY=your-api-key
WIDGET_COMMUNITY_ID=your-community-id
WIDGET_ID=your-widget-id
WIDGET_TYPE=store

2Accept user email from an authenticated request

The email must come from your verified session or auth middleware - never from a query parameter or request body that the client controls directly.

3Fetch a JWT from Returning.AI

POST the user's email along with your API key to the signin endpoint.

POST /widget/{communityId}/signin
Headers:
  Content-Type: application/json
  returningai-api-key: YOUR_API_KEY
  email: user@example.com

Response:
  { "token": "eyJhbGciOiJIUzI1NiIs..." }

4Fetch widget HTML

GET the widget page. The HTML comes with an empty token placeholder in the __WIDGET_INIT__ config.

GET /{widget-type}/{widgetId}?color=light

Response:
  <!DOCTYPE html>
  <html>
    <head>...</head>
    <body>
      <script>
        window.__WIDGET_INIT__ = { token: "", ... };
      </script>
      ...
    </body>
  </html>

5Inject token into HTML and serve

Replace the empty token placeholder with the JWT, then send the modified HTML to the browser.

// Replace the empty token placeholder with the real JWT
html = html.replace(/token:\s*""/, `token: "${safeToken}"`);

How this differs from Widget SDK and Iframe

Unlike the Widget SDK or Iframe methods, the server-side proxy gives you complete control over the authentication flow but requires more backend work. The tradeoff is maximum security for additional implementation effort.

Guides