# Quickstart

> From an empty account to a billed response, in four steps.

Section: Getting Started. Canonical page: https://routehook.ai/docs/quickstart

Every call goes to `https://api.routehook.ai/v1`, carries an API key in the `Authorization` header and draws against a prepaid balance. If your code already talks to an OpenAI-compatible endpoint, the integration is two lines: the base URL and the key.

## Four steps

1. **Create an account and add credits**

   There is no free tier and no trial balance. A key with nothing behind it answers `402 insufficient_credits` on its first call. The smallest top-up is $5, and credits spend across every live model.

2. **Mint a key**

   Keys are created in the dashboard and shown once. Copy it straight into a secret manager or an environment variable. Choose live or test at creation; a key's environment cannot be changed afterwards.

3. **Make the call**

   Point an OpenAI client at the base URL, or send the JSON yourself. `model` takes a slug from `GET /v1/models`.

4. **Read what it cost**

   The response headers report what the call cost and how it went. `GET /v1/credits` shows the balance after it.

## Set the key

**shell**

```shell
export ROUTEHOOK_API_KEY="sk_live_..."
```

## The one-line switch

**JavaScript**

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.routehook.ai/v1",
  apiKey: process.env.ROUTEHOOK_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "openai/gpt-5.4-mini",
  messages: [{ role: "user", content: "Name three uses for a gateway." }],
});

console.log(completion.choices[0].message.content);
```

**Python**

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.routehook.ai/v1",
    api_key=os.environ["ROUTEHOOK_API_KEY"],
)

completion = client.chat.completions.create(
    model="openai/gpt-5.4-mini",
    messages=[{"role": "user", "content": "Name three uses for a gateway."}],
)

print(completion.choices[0].message.content)
```

## The model field is a slug

`model` is a catalogue slug (`openai/gpt-5.4-mini`, `anthropic/claude-fable-5`) not a bare vendor model name. `GET /v1/models` lists every slug with its category, status and rate, and needs no key, so a model picker can read it straight from a browser. A slug nothing can serve right now answers `409 model_unavailable` rather than quietly routing somewhere else.

## The same call, without an SDK

**cURL**

```bash
curl https://api.routehook.ai/v1/chat/completions \
  -H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-mini",
    "messages": [{ "role": "user", "content": "Name three uses for a gateway." }]
  }'
```

## What the call cost, and how it went

The body is OpenAI's, so the charge is not in it. That goes in the response headers, and `-i` on the request above shows them. They matter because a retry is otherwise invisible: a request that failed over reads as an ordinary 200, and these headers are the only place that shows. None of them names the host that answered, which upstream serves a call is not something this API reports.

| HEADER | EXAMPLE | WHAT IT REPORTS |
| --- | --- | --- |
| X-Routehook-Request-Id | req_7c41d9be | The id of this call. Quote it in a support report; pass it to GET /v1/generation. |
| X-Routehook-Attempts | 1 | How many attempts it took before one answered. |
| X-Routehook-Fallback | false | true when the first choice could not serve it and the chain fell through. |
| X-Routehook-Cost | 0.000103 | USD charged for this call. Absent on a stream. The charge is not known when the headers are written. |
| X-Routehook-Upstream-Latency-Ms | 812 | How long the upstream leg took, excluding our own routing. |

**Response headers**

```http
HTTP/1.1 200 OK
Content-Type: application/json
X-Routehook-Request-Id: req_7c41d9be
X-Routehook-Attempts: 1
X-Routehook-Fallback: false
X-Routehook-Cost: 0.000103
X-Routehook-Upstream-Latency-Ms: 812
```

> **Note: A stream reports its cost inside the stream**
>
> Headers are written before the first token, so a streamed response carries no cost header. Send `stream_options: { include_usage: true }` and the final frame carries `usage.cost`, or keep the request id and read the charge from `GET /v1/generation` afterwards.

## Watch the balance move

One route reports the money side of the account. `balance` is settled cash, `held` is what requests in flight have reserved against it, and `available` is what the next request may spend, which is why a call can be refused while the balance still looks healthy.

## GET /v1/credits

Read the account balance, the amount held by requests in flight and what is left to spend.

Status: Available. Authentication: any live API key on the account.

**cURL**

```bash
curl https://api.routehook.ai/v1/credits \
  -H "Authorization: Bearer $ROUTEHOOK_API_KEY"
```

**200 OK**

```json
{
  "data": {
    "total_credits": 250.0,
    "total_usage": 128.44,
    "balance": 121.56,
    "held": 0.42,
    "available": 121.14,
    "credit_limit": 0,
    "currency": "USD"
  }
}
```

> **Warning: There is no sandbox**
>
> Every key is a live key, and the call you make while reading this costs what it would cost in production. There is no free tier and no test mode to fall back on, so put a spend cap on any key you are experimenting with (`GET /v1/key` reports it as `limit` and what is left as `limit_remaining`), and watch the balance move on `GET /v1/credits`.

## Let an agent wire it up

Working in Claude Code, Codex or Gemini CLI? Paste the command below. It points the agent at `/llms.txt`: the whole integration written for a machine: base URL, auth header, where model slugs come from, every error code and which of them are worth retrying.

**paste into your agent**

```bash
Read https://routehook.ai/llms-full.txt and wire this project
up to Routehook exactly as it specifies. Use my ROUTEHOOK_API_KEY from the
environment, pick a model slug from GET /v1/models rather than guessing one,
and only add retry logic for 429 and 503.
```

> **Note: Why point at the file rather than describe the API**
>
> An agent that has to infer an integration will produce one that compiles. The brief removes the inference: it names the base URL, the one route that takes `x-api-key` instead of `Authorization`, and the rule that only 429 and 503 are worth retrying. The three things agents most reliably get wrong here. It is served as plain text, so `curl`, a browser and a fetch tool all read it the same way.

## Where to next

- [Agent brief (/llms.txt)](/llms.txt): The whole integration as one plain-text file, written for a coding agent to read.
- [Chat Completions](/docs/chat): The full request shape, including fallback model lists.
- [Streaming](/docs/streaming): Server-sent events frame by frame, including the failure case.
- [Errors](/docs/errors): Six codes, one envelope, and what to do about each.
- [Cost Tracking](/docs/cost-tracking): Balance, holds, and what a single request cost.
