# Quickstart

> Start a local gateway, connect a provider, and make an authenticated request.

URL: https://boelabs.com/en/docs/ai-gateway/quickstart
Language: English

This guide creates a local gateway with an OpenAI deployment named `general` and a virtual key
for your application. If the gateway is already running, start with
[Your first deployment](https://boelabs.com/docs/ai-gateway/first-deployment).

## Before you start

Install Bun 1.4+ and Docker Compose. You also need an OpenAI API key with access to the model
used below. The Compose commands start Postgres 18 and Redis 8 locally.

<Callout type="warn" title="Keep operator credentials private">

The **master key** administers AI Gateway. A **provider API key** authenticates AI Gateway with the
upstream provider. Your application receives a separate **virtual key**; do not give it either
operator credential.

</Callout>

<Steps>
<Step>

## Run the gateway

```bash
git clone https://github.com/boelabs/ai-gateway.git && cd ai-gateway
bun install
docker compose -f docker/compose.yaml -f docker/compose.local.yaml up -d postgres redis
```

Create `apps/api/.env` (see `apps/api/.env.example`) with at least:

```bash
PORT=4000
NODE_ENV=development
MASTER_KEY=replace-with-at-least-32-random-characters
ENCRYPTION_KEYRING={"primary":"64_hex_chars_32_bytes"}   # value: openssl rand -hex 32
ACTIVE_ENCRYPTION_KEY_ID=primary
DATABASE_URL=postgres://gateway:gateway@localhost:5432/ai_gateway
REDIS_URL=redis://localhost:6379
```

Apply migrations and start it:

```bash
bun run --filter @boelabs/ai-gateway-api db:migrate
bun run --filter @boelabs/ai-gateway-api dev
```

Confirm it is up:

```bash
curl http://localhost:4000/health/ready   # 200 once Postgres + Redis + extensions are healthy
```

For the rest of this page:

```bash
export BASE=http://localhost:4000
export MASTER_KEY=replace-with-at-least-32-random-characters
```

</Step>
<Step>

## Register a deployment

A **deployment** binds a public model name to an adapter, an upstream model, and credentials. Create
one with the master key — the provider API key goes inline and is encrypted at rest:

```bash
curl -X POST $BASE/admin/deployments \
  -H "Authorization: Bearer $MASTER_KEY" -H "content-type: application/json" -d '{
    "publicModel": "general",
    "adapterKey": "openai",
    "upstreamModel": "gpt-5.5",
    "credentials": { "apiKey": "sk-..." }
  }'
```

`general` is now a public model. To inspect the resolved operations and transports **before saving**, send the same body to `POST /admin/deployments/resolve`.

</Step>
<Step>

## Issue a virtual key

Clients never use the master key. Mint a scoped virtual key instead — `allowedModels` limits which
public models it may call (`[]` or omitted = all):

```bash
curl -X POST $BASE/admin/keys \
  -H "Authorization: Bearer $MASTER_KEY" -H "content-type: application/json" -d '{
    "name": "my-app",
    "allowedModels": ["general"]
  }'
```

The response contains the plaintext key. Store it now; you can copy it again later from the admin
API or the dashboard, unless the key is created with `"private": true`:

```json
{ "data": { "id": "…", "keyPrefix": "sk-abcdefg", "key": "sk-…", "allowedModels": ["general"] } }
```

See [Virtual keys](https://boelabs.com/docs/ai-gateway/virtual-keys) for budgets, RPM/TPM limits, and rotation.

</Step>
<Step>

## Make a call

Set `model` to the public name and use the virtual key returned by the previous step.

<Tabs items={["curl", "Python"]}>
<Tab value="curl">

```bash
curl -X POST $BASE/v1/chat/completions \
  -H "Authorization: Bearer sk-..." -H "content-type: application/json" -d '{
    "model": "general",
    "messages": [{ "role": "user", "content": "Say hello in one word." }]
  }'
```

</Tab>
<Tab value="Python">

Install the SDK with `pip install openai`.

```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:4000/v1", api_key="sk-...")
resp = client.chat.completions.create(
    model="general",
    messages=[{"role": "user", "content": "Say hello in one word."}],
)
print(resp.choices[0].message.content)
```

</Tab>
</Tabs>

A successful response contains an assistant message in `choices[0].message.content`.
If the request fails, check the model name, virtual key scope, and provider credentials.

The same key and base URL also serve `/v1/responses`, `/v1/messages` (Anthropic shape),
`/v1/embeddings`, `/v1/images/*`, and `/v1/audio/transcriptions`. Every response carries an
`x-request-id`; virtual-key requests with limits also return `x-ratelimit-*` headers.

</Step>
</Steps>

## Next steps

- [Concepts](https://boelabs.com/docs/ai-gateway/concepts) — the mental model behind public models, pools, adapters, and transports.
- [Architecture](https://boelabs.com/docs/ai-gateway/architecture) — the same request, traced through every layer in precise order.
- [Creating deployments](https://boelabs.com/docs/ai-gateway/creating-deployments) — every provider and custom-model body.
- [Fallbacks](https://boelabs.com/docs/ai-gateway/fallbacks) — make a public model survive a provider outage.
- [Troubleshooting](https://boelabs.com/docs/ai-gateway/troubleshooting) — what a `400`/`401`/`403`/`429`/`503` means and how to fix it.

