> This page location: Backend > AI Gateway > Reference > Authentication
> Full Neon documentation index: https://neon.com/docs/llms.txt

> Summary: AI Gateway uses Neon bearer credentials with the ai_gateway:invoke scope. Credentials are scoped to a branch and its descendants, so a credential created on your main branch works in all preview branches. No provider API keys are required.

# AI Gateway authentication

How Neon credentials work with AI Gateway

**Coming Soon: Private Preview**

This feature is in private preview: it's not ready for production use, and it may be briefly unavailable as we deploy updates. To get access, [sign up here](https://neon.com/blog/were-building-backends#access).

AI Gateway uses Neon bearer credentials, the same credential system as [Neon Storage](https://neon.com/docs/introduction). No provider API keys are needed.

## Creating a credential

A credential must include the `ai_gateway:invoke` scope.

**Console**

In the Neon Console, select your branch and click **Credentials** under **APP BACKEND** in the sidebar. Click **Create credential**, give it a name, and check **ai_gateway:invoke**.

After creation, the credential is shown once. Copy the snippet or click **Download .env** before closing. The snippet includes all four gateway env vars (see [Environment variables](https://neon.com/docs/ai-gateway/authentication#environment-variables) below).

To view or revoke credentials later, return to the **Credentials** page and use the action menu (⋮) next to the credential.

**API**

```bash
curl -X POST "https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/credentials" \
  -H "Authorization: Bearer $NEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopes": ["ai_gateway:invoke"], "principal_type": "user"}'
```

The response includes an `api_token` field. Store it as an environment variable:

```bash
export NEON_AI_GATEWAY_TOKEN=nt_live_...
```

## Pull credentials with neonctl

For local development, `neonctl env pull` writes your AI Gateway credentials to your `.env` file automatically, with no manual copy-paste from the API response:

```bash
neonctl env pull .env
```

This populates `NEON_AI_GATEWAY_TOKEN` and `NEON_AI_GATEWAY_BASE_URL` for the current branch alongside your database connection string. Running `neonctl config apply` or `neonctl deploy` also auto-pulls credentials after a successful apply. To check current credential status:

```bash
neonctl config status
```

For production deployments, use the [API-based workflow](https://neon.com/docs/ai-gateway/authentication#creating-a-credential) to create named credentials with optional expiry.

## Using your credential

Pass your credential as a bearer token on every request:

```
Authorization: Bearer <your-credential>
```

When using an AI SDK, set this as the `apiKey` parameter:

**TypeScript**

```typescript
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.NEON_AI_GATEWAY_TOKEN,
  baseURL: `${process.env.NEON_AI_GATEWAY_BASE_URL}/ai-gateway/mlflow/v1`,
});
```

**Python**

```python
from openai import OpenAI
import os

client = OpenAI(
    api_key=os.environ["NEON_AI_GATEWAY_TOKEN"],
    base_url=f"{os.environ['NEON_AI_GATEWAY_BASE_URL']}/ai-gateway/mlflow/v1",
)
```

## Environment variables

Neon provides four gateway env vars. Two follow OpenAI's standard naming so existing SDKs pick them up automatically. Two use `NEON_`-prefixed names that survive a user overriding the `OPENAI_*` variables with their own provider keys.

| Variable                   | Value                                                                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------- |
| `OPENAI_API_KEY`           | Bearer token (`nt_live_...`)                                                                            |
| `OPENAI_BASE_URL`          | Full OpenAI Responses dialect URL: `https://<branch-host>/ai-gateway/openai/v1` — **includes the path** |
| `NEON_AI_GATEWAY_TOKEN`    | Same bearer token as `OPENAI_API_KEY`                                                                   |
| `NEON_AI_GATEWAY_BASE_URL` | Bare branch host: `https://<branch-host>` — **no path**; append `/ai-gateway/<dialect>/v1` yourself     |

`OPENAI_BASE_URL` and `NEON_AI_GATEWAY_BASE_URL` point at different URLs. `OPENAI_BASE_URL` already includes `/ai-gateway/openai/v1`, so `new OpenAI()` picks it up with zero config and calls the Responses API. `NEON_AI_GATEWAY_BASE_URL` is the bare host — append the dialect path yourself:

```
NEON_AI_GATEWAY_BASE_URL + /ai-gateway/mlflow/v1   → chat completions (all providers)
NEON_AI_GATEWAY_BASE_URL + /ai-gateway/openai/v1   → OpenAI Responses API (= OPENAI_BASE_URL)
NEON_AI_GATEWAY_BASE_URL + /ai-gateway/anthropic   → Anthropic Messages API
NEON_AI_GATEWAY_BASE_URL + /ai-gateway/gemini      → Gemini generateContent API
```

## Credentials in Neon Functions

When your code runs inside Neon Functions, all four env vars are injected automatically. No credential creation step required:

| Variable                   | Value                                                   |
| -------------------------- | ------------------------------------------------------- |
| `NEON_AI_GATEWAY_TOKEN`    | Bearer token for the AI Gateway                         |
| `NEON_AI_GATEWAY_BASE_URL` | Branch gateway host with `https://` prefix, no path     |
| `OPENAI_API_KEY`           | Same value as `NEON_AI_GATEWAY_TOKEN`                   |
| `OPENAI_BASE_URL`          | Branch gateway host including the OpenAI Responses path |

`new OpenAI()` works with zero configuration because `OPENAI_API_KEY` and `OPENAI_BASE_URL` are already set:

```typescript
import OpenAI from 'openai';

// Reads OPENAI_API_KEY + OPENAI_BASE_URL from the environment automatically.
const client = new OpenAI();

const response = await client.responses.create({
  model: 'gpt-5-mini',
  input: 'What is Neon?',
});
```

For the chat completions endpoint or when you need credentials that survive a user overriding `OPENAI_*` with their own keys, use the `NEON_AI_GATEWAY_*` vars:

```typescript
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.NEON_AI_GATEWAY_TOKEN,
  baseURL: `${process.env.NEON_AI_GATEWAY_BASE_URL}/ai-gateway/mlflow/v1`,
});
```

## How branch binding works

Each credential is tied to the branch it was created on. It's valid for:

- That branch (the anchor branch)
- Any branch descended from it: preview branches, feature branches, CI branches

It's **not** valid for branches outside that lineage.

This means a credential created on your `main` branch works in all branches that were forked from `main`. A credential created on a feature branch only works within that feature branch's descendants.

```
main  ──── credential valid here
  └── preview/feature-x  ──── and here
        └── preview/sub-branch  ──── and here
staging  ──── credential NOT valid here (different lineage)
```

This design lets you use a single credential across your entire development workflow (local dev, preview deployments, and CI) without creating separate credentials for each environment.

## Common auth errors

| Error                     | Cause                                      | Fix                                                                                                                             |
| ------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized`        | Missing or invalid credential              | Check that `NEON_AI_GATEWAY_TOKEN` is set and contains the full token                                                           |
| `403 Forbidden`           | Credential lacks `ai_gateway:invoke` scope | Recreate the credential with the correct scope                                                                                  |
| `403 Forbidden`           | Branch not in credential lineage           | Use a credential created on this branch or an ancestor branch. The gateway returns: `credential not authorized for this branch` |
| `503 Service Unavailable` | Auth store temporarily unavailable         | Retry the request                                                                                                               |

## Rotating credentials

To rotate a credential: create a new one, update your environment variables, then revoke the old one.

To revoke from the Console, open the **Credentials** page and use the action menu (⋮) next to the credential. To revoke via the API:

```bash
curl -X DELETE "https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/credentials/{token_id}" \
  -H "Authorization: Bearer $NEON_API_KEY"
```

---

## Related docs (Reference)

- [Troubleshooting](https://neon.com/docs/ai-gateway/troubleshooting)
