# Authentication | JavaScript AI Autocomplete Docs

> For the complete documentation index, see [llms.txt](https://ai-autocomplete.com/llms.txt). A markdown version of any page on this site is available by appending `.md` to its URL path — the homepage is at `/index.md`.

Authentication guide for integrating the AI Autocomplete SDK with JavaScript. Code examples, setup, and API reference.

Source: https://ai-autocomplete.com/docs/vanilla/authentication

---

Two key types. Public keys plug straight into the SDK from the browser. Secret keys live on your server and mint short-lived access tokens that the SDK uses — recommended for production.

[Manage your keys](https://ai-autocomplete.com/account/keys)

## Choosing a mode

Public key mode is the fastest path: drop a public key into apiConfig and you're done. Secret key + access token mode is what we recommend for production — your server keeps the secret key, mints short-lived access tokens, and the SDK handles refresh transparently.

## Public key mode

Use a public key when calling the SDK directly from the browser. Public keys are scoped, rate-limited, and safe to ship in client-side bundles. This is the fastest path to a working integration.

**auth-public-key.ts**

```tsx
new AIAutocomplete(container, {
  apiConfig: {
    // Public key (pk_v1_...) — safe in client bundles
    apiKey: "pk_v1_your_public_key",
    endpoint: "https://api.ai-autocomplete.com/api/suggest",
    headers: { "X-Custom": "v" },
  },
  onSubmit: handleSubmit,
});
```

Security

Public keys are safe in client bundles, but for production we recommend the secret key + access token flow below — short-lived tokens limit blast radius if a credential leaks. Never put a secret key in client code.

## Secret key + access token (recommended)

Keep your secret key on your backend server. The server — never the browser — exchanges it for a short-lived access token. Your frontend fetches the token from your own backend route, then hands it to the SDK via getAccessToken. The SDK handles refresh, retries, and concurrency dedupe — the secret key never reaches the browser.

### On your server

On your backend, expose a route that exchanges your secret key for a short-lived access token by calling the endpoint below. Forward the response to the browser; the SDK reads it through getAccessToken. The secret key must never reach the browser — keep it in a server-side environment variable with no client-side prefix.

POST `https://api.ai-autocomplete.com/api/auth/token`

Headers

`Authorization: Bearer <sk_v1_…>` — your secret key.

Body

`product_id` — the UUID of the product the token is for. Visible in the URL of the product editor at /edit.

Request body

```json
{
  "product_id": "<uuid>"
}
```

200 OK

```json
{
  "access_token": "at_v1_AbCdEfGh...",
  "expires_at": 1775464738,
  "token_type": "Bearer"
}
```

expires_at is unix seconds. Pass it through unchanged — the SDK normalizes it.

401 Unauthorized

```json
{
  "error": {
    "code": "INVALID_SECRET_KEY",
    "message": "The provided secret key is invalid"
  }
}
```

### In the browser

The frontend SDK calls a route on your own backend (e.g. /api/ac-token), reads the access token + expiry, and returns them to the SDK. No secret-key code lives here.

**autocomplete.ts (runs in the browser)**

```tsx
// BROWSER-ONLY. No secret key here — only the URL of your own
// backend route, which holds the secret key.
new AIAutocomplete(container, {
  apiConfig: {
    type: "accessToken",
    getAccessToken: async () => {
      const res = await fetch("/api/ac-token");
      const { access_token, expires_at } = await res.json();
      return { accessToken: access_token, expiresAt: expires_at };
    },
    endpoint: "https://api.ai-autocomplete.com/api/suggest",
  },
  onSubmit: handleSubmit,
});
```

How refresh works

On 401, the SDK calls getAccessToken once and retries. Concurrent 401s share a single refresh. Tokens are refreshed proactively 30 seconds before expiresAt.
