> Legacy approach
>
> For new integrations, use the [Widget SDK with Access Key Embed](https://docs.returning.ai/widget-sdk/auth-access-key.md).

# Server-Side Proxy

This legacy integration fetches widget HTML on your server, injects a session token, and serves it to the browser. Keep the API key on the server; the injected token reaches the browser. Use this approach only as approved for your supplied setup.

> Integration method, not authentication method
>
> Server-Side Proxy is one of three ways to **integrate** Returning.AI widgets (alongside the [Widget SDK](https://docs.returning.ai/widget-sdk.md) and [Embed Script](https://docs.returning.ai/embed-script.md)). It uses legacy [Token Authentication](https://docs.returning.ai/embed-script/auth-token.md) under the hood. If you haven't read the Token Auth guide yet, start there first.

## Architecture

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

```bash
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 server | Goes to the browser |
| --- | --- |
| `WIDGET_API_KEY` | JWT (short-lived, user-scoped) |
| `WIDGET_COMMUNITY_ID` | Widget ID |
| Returning.AI API requests | Rendered 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`

```bash
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.

```bash
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.

```bash
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.

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

> How this differs from Widget SDK and Embed Script
>
> Unlike the Widget SDK or Embed Script 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

Implementation

Complete code examples in Node.js, PHP, and Python.

[Open](https://docs.returning.ai/server-proxy/implementation.md)

Security

Hardening guide: auth checks, rate limiting, CORS, SSRF prevention, and a production checklist.

[Open](https://docs.returning.ai/server-proxy/security.md)
