# Apigator API - Integration Guide (agent-friendly)

> Agent note: If you are an AI agent wiring up a client for your user, follow this
> file verbatim. Base URL, auth, and model id rules are all below. Do not invent
> endpoints or model names. After setup, call GET /v1/models to confirm access.

Apigator is an OpenAI-compatible AI gateway. Point any OpenAI SDK / client at it,
pass an Apigator API key, and call hundreds of models through one endpoint. Billing
is pay-per-token from the user's Apigator credit balance.

## Agent quickstart — zero to first call

No browser, no card, no human top-up. Already have a key? Skip to step 3.

```bash
# 1) Request a sign-in code (emailed to that address — you need to read that inbox)
curl -X POST https://api.apigator.ai/agent/signup \
  -H "Content-Type: application/json" -d '{"email":"you@example.com"}'
# -> {"verifyToken":"...","message":"Verification code sent to you@example.com."}

# 2) Verify with the 6-digit code -> your API key
curl -X POST https://api.apigator.ai/agent/verify \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","code":"123456","verifyToken":"..."}'
# -> {"key":"sk-...","balance":0.042,"accessToken":"...", ...}
#    New accounts start with a $0.042 credit (≈ 1M Jev decision-model tokens).

export APIGATOR_KEY=sk-...

# 3a) Chat — OpenAI-compatible, free model (bills $0)
curl https://api.apigator.ai/v1/chat/completions \
  -H "Authorization: Bearer $APIGATOR_KEY" -H "Content-Type: application/json" \
  -d '{"model":"free/nemotron-nano-30b","messages":[{"role":"user","content":"Hello"}]}'

# 3b) Decide — Jev returns typed answers + calibrated probabilities (paid from the free credit)
curl https://api.apigator.ai/typesafe/v1/systemone \
  -H "Authorization: Bearer $APIGATOR_KEY" -H "Content-Type: application/json" \
  -d '{"model":"jev-latest","state":"I was charged twice, refund me now!",
       "questions":{"intent":{"type":"choice","instructions":"What does the user want?",
                              "criteria":{"refund":null,"cancel":null,"question":null}},
                    "is_angry":{"type":"noul","instructions":"The user is angry"}}}'
# -> {"answers":{"intent":{"type":"choice","choice":"refund","probabilities":{...},"confidence":...},
#                "is_angry":{"type":"noul","noul":...}}, ...}
```

Gotchas: using Python's built-in `urllib`? Set a `User-Agent` header, or you get `403 error code: 1010`. The code is single-use and a couple of wrong tries burns it — call `/agent/signup`
again for a fresh one. Check balance anytime with `GET /agent/account` (Bearer the
`accessToken`). Pick more models at https://apigator.ai/models-catalog.json; details for
each step are in the sections below.

## Base URL

```
https://api.apigator.ai/v1
```

One endpoint for everyone. The environment is determined by the key, not the URL
(Stripe-style). This is the OpenAI-compatible surface only; Claude Code CLI uses a
separate endpoint (see "Claude" below).

## Authentication

Bearer token in the `Authorization` header. The key is an Apigator credits key
(`sk-...`), obtained by the user at https://apigator.ai (Dashboard / Billing). One
key works across all models and draws from the same credit balance.

```
Authorization: Bearer sk-YOUR_APIGATOR_KEY
```

Send a `User-Agent` header. Requests with Python's built-in `urllib` default agent
(`Python-urllib/3.x`) are blocked at the edge with `403 error code: 1010`; curl, `requests`,
the OpenAI SDKs and Node are fine. With `urllib`, add e.g. `User-Agent: my-agent/1.0`.

## Getting a key (shortest path)

1. Go to https://apigator.ai and sign in with a **MixerBox ID** (email + a one-time code
   emailed to that address — no password). An autonomous agent needs a readable inbox to
   read the code.
2. A credits key (`sk-...`) is **auto-provisioned** on first sign-in — there is no separate
   "create key" step. Copy it from the Dashboard → **Keys** (Reveal → Copy).
3. The key works immediately for the **free models** (below), and every new account
   starts with a **$0.042 sign-up credit** — about 1M input tokens of the
   [Jev decision model](#decision-model-typesafe-jev) (https://apigator.ai/jev).

> **Agent note — you do NOT need a human to top up to start.** A brand-new key needs no
> top-up for the free models, and the sign-up credit already covers ~1M Jev tokens. Use them for setup, smoke tests,
> and any workload a free model can handle. Only ask a human to top up if the task
> genuinely needs a *paid* model (e.g. `openai/gpt-5.2`, Claude). Don't block on billing
> when a free model would do.

### Agent self-signup (no browser)

An autonomous agent can register with two API calls instead of driving the sign-in page.
Email verification still applies — a one-time code is sent to your address, so the agent
needs a readable inbox.

1. Request a code:

```bash
curl -X POST https://api.apigator.ai/agent/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}'
# -> {"verifyToken":"...","message":"Verification code sent to you@example.com."}
```

2. Read the code from your inbox.

3. Verify and receive your key:

```bash
curl -X POST https://api.apigator.ai/agent/verify \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","code":"123456","verifyToken":"..."}'
# -> {"key":"sk-...","email":"you@example.com","balance":0.042,"docs":"https://apigator.ai/api.md"}
```

The `sk-...` key is your Apigator API key. A new account's `balance` is the $0.042 sign-up
credit (≈ 1M Jev tokens; an existing account shows its current balance). Free models work at
any balance; top up only when a task needs a paid model. `/agent/verify` also returns an `accessToken`
(valid ~7 days) for the account endpoint below.

### Agent account status (read-only)

Check your balance/usage with the `accessToken` from `/agent/verify`:

```bash
curl https://api.apigator.ai/agent/account \
  -H "Authorization: Bearer <accessToken from /agent/verify>"
# -> {"balance":9.94,"spent":1.06,"budget":11,
#     "key":{"last4":"aGbQ","createdAt":...,"lastUsedAt":...},"claudeAllowed":true}
```

Read-only — top-ups need a human (a card) at https://apigator.ai. When this returns
`401`, the accessToken expired: just re-run `/agent/signup` + `/agent/verify` (you have
the inbox) for a fresh one.

## Quick start (drop-in OpenAI client)

Python:

```python
from openai import OpenAI

client = OpenAI(base_url="https://api.apigator.ai/v1", api_key="sk-...")

resp = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
```

Node:

```js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.apigator.ai/v1", apiKey: "sk-..." });
const r = await client.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Hello!" }],
});
```

curl:

```bash
curl https://api.apigator.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello!"}]}'
```

## Choosing a model id

Model ids are clean `trainer/model` names (the trainer is the model's author — like
OpenRouter), never a serving-provider prefix. Examples: `gpt-4o`, `openai/gpt-5.2`,
`meta-llama/llama-3.3-70b-instruct`, `deepseek/deepseek-chat`, `google/gemini-2.5-flash`,
`z-ai/glm-5`, `nvidia/nemotron-3-nano-30b-a3b`. Routing (which backend actually serves a
model, and cheapest-first fallback) is handled internally — you never pick a provider.

Everything except Claude is open to every key by default. New models become available
automatically.

### Free models (no balance needed)

A few models bill $0 and work on a brand-new key **before you top up** — ideal for a first
smoke test. Just use the `free/` id:

- `free/nemotron-nano-30b`
- `free/nemotron-super-120b`
- `free/gemma-4-31b`

Paid models require a balance (top up in the Dashboard); calling one on a $0 balance
returns a clear budget error.

## Picking a model by capability (video / audio / image input / function calling)

Each model in `https://apigator.ai/models-catalog.json` carries a **verified** `capabilities`
object (probed on our gateway, not copied from upstream claims):

```json
"capabilities": { "input": ["text","image","video"], "video": "native", "audio_in": false }
```

- `input` — which inputs the model actually accepts on our gateway.
- `video` — **`native`** (ingests the real video: frames + audio + timing — e.g. Gemini),
  **`frames`** (samples still frames only — picture, NO sound), or **`none`**.
- `audio_in` — whether it processes an audio track.
- `tools` — function calling, verified on our gateway: **`chat`** (send `tools` on
  `/v1/chat/completions` as usual), **`responses`** (only accepted on `/v1/responses`), or
  **`none`** (the upstream rejects tools outright — e.g. `openai/gpt-5-search-api`,
  `perplexity/sonar-pro-search`). Absent = not yet probed. **Agents: check this before sending
  `tools`**; a `none` model returns 400/404 for every tool-carrying request.

**To analyse a video WITH speech/sound, use a `video: "native"` model** (e.g.
`google/gemini-3-flash-preview`). A `video: "frames"` model sees pictures only. A model not
listing video in `input` will reject video input. Don't trust a bare "multimodal" label — read
`capabilities`.

## Discover available models

```bash
curl https://api.apigator.ai/v1/models -H "Authorization: Bearer sk-..."
```

Returns the OpenAI-standard `{"data":[{"id":...}]}` list. This is the live superset of
addressable ids; for a few wildcard providers it may include an id the upstream account
has not enabled yet — calling it returns a clear `404 model not available`, so just pick
another.

The authoritative, account-accurate catalog (only ids that actually resolve, with
pricing) is the browsable list at https://apigator.ai/models, backed by
https://apigator.ai/models-catalog.json (stable JSON: `{"models":[{"id","name",
"provider","ctx","in","out"}]}`) — prefer this when programmatically choosing a model.

## Streaming

Standard OpenAI SSE streaming works. Set `"stream": true` and read the
`data:`-prefixed chunks. No special handling required.

## Claude (Anthropic models)

Claude is access-controlled (off by default; granted per user via an allowlist) and
is served on a SEPARATE Anthropic-compatible endpoint for the Claude Code CLI:

```
https://claude.apigator.ai
```

See https://apigator.ai/claude-cli (or /claude-cli.md) for that setup. Calling a
Claude model on this OpenAI endpoint without allowlist access returns
`403 claude_not_allowed`.

## Endpoints by modality

Not everything is chat. Each model on https://apigator.ai/models lists the endpoint it
uses; pick the right one. Same base URL, same Apigator key.

| Modality | Endpoint | Model id example |
|----------|----------|------------------|
| Chat | `POST /v1/chat/completions` | `gpt-4o` |
| Image generation | `POST /v1/images/generations` | `openai/gpt-image-1`, `fal_ai/fal-ai/flux/schnell` |
| Text-to-Speech | `POST /v1/audio/speech` | `elevenlabs-tts` |
| Speech-to-Text | `POST /v1/audio/transcriptions` | `groq/whisper-large-v3` |
| Embeddings | `POST /v1/embeddings` | `openai/text-embedding-3-small` |
| Video generation (async) | `POST /v1/videos` | `gemini/veo-3.1-fast-generate-preview` |
| Decision (structured answers, not chat) | `POST /typesafe/v1/systemone` (note: outside `/v1`) | `jev-latest` — see [Decision model](#decision-model-typesafe-jev) |

## Image generation

```bash
curl https://api.apigator.ai/v1/images/generations \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-image-1","prompt":"a red crocodile mascot","n":1}'
```

Returns the OpenAI-standard `{"data":[{"url":...}]}` (or `b64_json`). Fal models use
`fal_ai/<path>` (e.g. `fal_ai/fal-ai/flux/schnell`). Billed per image, not per token.

## Embeddings

```bash
curl https://api.apigator.ai/v1/embeddings \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/text-embedding-3-small","input":"hello world"}'
```

Returns `{"data":[{"embedding":[...]}]}` (OpenAI standard).

## Audio (TTS, Speech-to-Text, Music)

- Text-to-Speech: `POST /v1/audio/speech`, model `elevenlabs-tts`; `voice` MUST be an
  ElevenLabs voice id (not a name) or it returns 500.
- Speech-to-Text: `POST /v1/audio/transcriptions` (Whisper/AssemblyAI).
- Music: `POST https://litellm.mixerbox.ai/elevenlabs/v1/music`.

Full audio guide (agent-friendly): https://apigator.ai/audio.md

## Video generation

OpenAI-style async Videos API (create job -> poll -> download). Verified working with
Veo 3.1 Fast. Billed per second (Veo 3.1 Fast ~ $0.15/s; min 4s, so ~$0.60/clip).

```bash
# 1) create a job
curl https://api.apigator.ai/v1/videos \
  -H "Authorization: Bearer sk-..." \
  -d '{"model":"gemini/veo-3.1-fast-generate-preview","prompt":"a red crocodile mascot waving","seconds":"4"}'
# -> {"id":"video_...","status":"queued"}

# 2) poll until status == "completed"
curl https://api.apigator.ai/v1/videos/video_... -H "Authorization: Bearer sk-..."

# 3) download the mp4
curl https://api.apigator.ai/v1/videos/video_.../content -H "Authorization: Bearer sk-..." --output out.mp4
```

## Music generation (preview)

PREVIEW. ElevenLabs Music works today as a passthrough:
`POST https://litellm.mixerbox.ai/elevenlabs/v1/music`. Other music models (e.g. Lyria)
are pending integration/access. No OpenAI standard exists for music yet.

## Decision model (TypeSafe Jev)

**Use Jev instead of a chat model when your code needs a decision, not prose** — classify,
route, score, filter, guardrail. Jev takes a `state` (any text or JSON) and a map of typed
`questions` you name, and returns one typed answer per question with calibrated
probabilities: no prompt engineering, no output parsing, ~100 ms. Ask many questions in one
call (one round trip, one bill).

| Question `type` | Asks | Answer fields |
|-----------------|------|---------------|
| `choice` | Pick one option from `criteria` (map option -> description, up to 255) | `choice`, `probabilities` (per option), `confidence` |
| `score` | Rate against ordered `criteria` levels (array, 2-10) | `score` (0-based, can land between levels), `legend`, `probabilities`, `confidence` |
| `noul` | Probability a yes/no statement is true (`criteria` optional: `{"true":..., "false":...}`) | `noul` (0 = no, 1 = yes) |

- Endpoint: `POST https://api.apigator.ai/typesafe/v1/systemone` (NOT under `/v1`, and NOT
  OpenAI-shaped — don't use an OpenAI SDK client for it). Same Apigator key, same balance.
- Model: `jev-latest`.
- Price: $0.042 per 1M input tokens, output free. **Every new account starts with a $0.042
  credit ≈ 1M Jev tokens** — no top-up needed to try it.
- Overview + comparison with chat models: https://apigator.ai/jev

Request:

```bash
curl https://api.apigator.ai/typesafe/v1/systemone \
  -H "Authorization: Bearer sk-..." -H "Content-Type: application/json" \
  -d '{
    "model": "jev-latest",
    "state": "Help! My payouts have been failing for 3 days.",
    "questions": {
      "department":  {"type": "choice", "instructions": "Which team should handle this?",
                      "criteria": {"billing": "Payments, invoicing, refunds",
                                   "technical": "Bugs, outages, integrations",
                                   "sales": "Pricing, upgrades, new accounts"}},
      "is_urgent":   {"type": "noul", "instructions": "Does this convey urgency?"},
      "frustration": {"type": "score", "instructions": "How frustrated is the customer?",
                      "criteria": ["Calm", "Frustrated", "Very angry"]}
    }
  }'
```

Response (answers come back under the ids you chose):

```json
{
  "model": "jev-1.13.0",
  "answers": {
    "department":  {"type": "choice", "choice": "billing",
                    "probabilities": {"billing": 0.88, "technical": 0.12, "sales": 0.0},
                    "confidence": 0.82},
    "is_urgent":   {"type": "noul", "noul": 0.95},
    "frustration": {"type": "score", "score": 1.04,
                    "legend": {"0": "Calm", "1": "Frustrated", "2": "Very angry"},
                    "probabilities": {"0": 0.0, "1": 0.96, "2": 0.04}, "confidence": 0.94}
  },
  "usage": {"input_tokens": 402, "output_tokens": 73}
}
```

Keep the decision logic in your code — threshold the probabilities / confidence:

```python
a = resp["answers"]
if a["is_urgent"]["noul"] > 0.8: page_on_call()
if a["department"]["confidence"] > 0.7: assign(a["department"]["choice"])
else: send_to_human()
```

Official SDKs (`pip install typesafe-sdk`, npm `typesafe`): set
`TYPESAFE_BASE_URL=https://api.apigator.ai/typesafe` and `TYPESAFE_API_KEY=<your Apigator key>`.
Full reference (question types, confidence, patterns, cookbooks):
https://docs.typesafe.ai/llms.txt · API: https://docs.typesafe.ai/api

## Errors

Standard OpenAI-style JSON error bodies. Common cases:

- `401` - missing or invalid key.
- `403 claude_not_allowed` - Claude not enabled for this key (ask MixerBox to
  allowlist the user).
- `400` - bad request (e.g., a provider-specific constraint such as Perplexity's
  minimum `max_tokens`).

## Notes

- One key, one balance, pay per token. No per-seat fees, no subscription.
- The base URL never changes per environment; keys distinguish environments.
- This file is published at https://apigator.ai/api.md for agents to fetch.
