# AI

> An OpenAI-compatible gateway to every major lab's models, for chat, embeddings, images, video, speech and transcription, metered per call.

One metered entry point for language models, embeddings, images, video, text to speech and speech to text. It is **OpenAI-compatible** under `/v1/ai`, so the OpenAI SDK, the AI SDK (`@ai-sdk/openai`) and most agents work by changing the base URL. Pick a Vibiz model by what it is for (`vibiz/fast`, `vibiz/smart`, `vibiz/image`…), or any lab's model by its own id (`anthropic/claude-opus-5.5`, `moonshotai/kimi-k3.1-pro`…). Either way exactly that model runs: Vibiz never swaps in another one.

```ts
import OpenAI from 'openai';
const openai = new OpenAI({ baseURL: process.env.VIBIZ_AI_BASE_URL, apiKey: process.env.VIBIZ_AI_API_KEY });
const stream = await openai.chat.completions.create({ model: 'vibiz/fast', messages, stream: true });
```

## Keys

- **Account key** (`vz_sk_…`): agents and scripts. Add `X-Vibiz-Project: prj_…` to bill a project.
- **Project AI key** (`vz_ai_…`): `vibiz add ai`. The app's env gets `VIBIZ_AI_BASE_URL`, `VIBIZ_AI_API_KEY` and the aliases `OPENAI_BASE_URL`, `OPENAI_API_KEY`, so `new OpenAI()` works unchanged.
- **App key**: `new OpenAI(vibiz.ai)` with `@vibiz/app`.

## Models and pricing

A Vibiz model is a stable name for what it is for. Each runs **one** lab model, named below and in every answer (`served_model`); we move a tier to a newer model as labs ship them, and the change shows here. If that model is down, the call fails (`model_unavailable` or `provider_unavailable`) rather than run a different model you didn't pay for.

| Vibiz model | Runs on | For | Price (USD) |
| --- | --- | --- | --- |
| `vibiz/fast` (default chat) | GPT-6 Luna (OpenAI) | chat, tool calls, JSON | $0.10 / $0.50 per million tokens in / out |
| `vibiz/lite` | DeepSeek V4.1 Flash (DeepSeek) | the cheapest chat, 1M token context | $0.14 / $0.42 |
| `vibiz/smart-mini` | Claude Haiku 5.5 (Anthropic) | quick, strong writing and coding | $0.10 / $0.50 |
| `vibiz/balanced` | Gemini 3.8 Flash (Google) | quality and price balanced, 1M token context | $0.825 / $4.125 |
| `vibiz/reasoning` | GPT-6.1 Terra (OpenAI) | thinks before it answers: math, logic, planning | $1 / $5 |
| `vibiz/smart` | Claude Sonnet 5.5 (Anthropic) | hard tasks: writing, coding, agents | $2 / $10 |
| `vibiz/pro` | Claude Opus 5.5 (Anthropic) | the strongest chat tier, long agent runs | $4 / $20 |
| `vibiz/embed` | text-embedding-3-small (OpenAI) | embeddings, 1536 dimensions | $0.02 per million tokens |
| `vibiz/embed-large` | text-embedding-3-large (OpenAI) | embeddings, 3072 dimensions | $0.13 per million tokens |
| `vibiz/image` | Nano Banana 2 (Google) | fast images, edits, consistent characters | about $0.067 per image |
| `vibiz/image-pro` | Nano Banana 2.1 (Google) | Google's newest image model | about $0.034 per image |
| `vibiz/image-text` | GPT-5.4 Image 2 (OpenAI) | long prompts, text rendered in the image | about $0.04 per image |
| `vibiz/video` | LTX-2.5 Fast (Lightricks) | 6 s clips at 720p with sound (jobs only) | $0.54 per video |
| `vibiz/voice` | Eleven v4 Turbo (ElevenLabs) | natural real-time voices | $0.04 per 1,000 characters |
| `vibiz/voice-pro` | Eleven v4 (ElevenLabs) | the most expressive voices | $0.08 per 1,000 characters |
| `vibiz/voice-lite` | Eleven Flash v2.5 (ElevenLabs) | the fastest voices, 32 languages | $0.04 per 1,000 characters |
| `vibiz/transcribe` | Scribe v2 (ElevenLabs) | speech to text, 99 languages | $0.22 per hour of audio |

These are the labs' list prices. A call is billed at what it really cost plus the AI fee in the price book (`markup_pct` on each model in `GET /v1/ai/models`). Some chat models charge more once a call's input passes a long-context threshold (`pricing.long_context`): GPT-6 models past 272,000 input tokens, Claude Haiku 5.5 past 100,000. Every JSON response carries `cost_micro_usd`, the exact cost of that call, and `served_model` (speech: the `X-Vibiz-Served-Model` header).

## All models

Beyond the Vibiz models, **every language model from every major lab** runs by its own id: OpenAI, Anthropic, Google, DeepSeek, xAI, Moonshot AI, Qwen, Mistral, Meta, MiniMax, Z.ai and more, over 400 in all, kept current as labs release them. Search them in the console (**AI models**), with `GET /v1/ai/models?all=1&q=kimi`, `vibiz ai models kimi`, or the `vibiz_ai_models` tool.

```json
{ "model": "moonshotai/kimi-k3.1-pro", "messages": [{ "role": "user", "content": "Hi" }] }
```

It runs exactly that model, at its list price plus the AI fee, and the answer names it. Usage groups a model behind a Vibiz model under that Vibiz model, and any other model under its own id. The media models above (`google/gemini-nano-banana-2.1`, `elevenlabs/v4`…) and older ones (`black-forest-labs/flux-schnell`, `openai/whisper-1`…) are called by id the same way. A model that isn't in the list can't be called (`invalid_request`, `param: "model"`), so a typo can't reach an expensive model.

Video takes `16:9` or `9:16` (`1:1` comes back as `16:9`).

## Limits

| | |
| --- | --- |
| Daily spend ceiling | $5 per account per UTC day by default (AI and actions together; an owner raises it in Billing); each call is held at its worst case before it runs |
| Credits | each call's worst case must fit in the account's balance (plus $0.50); out of credits: `402 payment_required` ([Billing](https://app.vibiz.app/docs/billing)) |
| Project budget | `PATCH /v1/projects/:id/ai { daily_budget_usd, end_user_daily_budget_usd }` |
| `max_tokens` | 1,024 when not given, at most 16,384 (per model) |
| Images | 4 per request, 256–2,048 px a side |
| Speech | 4,096 characters |
| Transcription | 25 MB upload |
| Embeddings | 2,048 inputs |
| Rate, account key | 120 requests and 400k tokens a minute, 20 in flight |
| Rate, project key | 60 requests and 200k tokens a minute (settable to 600 and 2M), 10 in flight (up to 50) |
| Media links | permanent with a project ([Files](https://app.vibiz.app/docs/files)); 24 hours without one |

## Use it

```bash
curl https://app.vibiz.app/v1/ai/chat/completions \
  -H "Authorization: Bearer $VIBIZ_API_KEY" -H 'content-type: application/json' \
  -d '{"model":"vibiz/fast","messages":[{"role":"user","content":"Name a bakery in three words."}]}'
```

```bash
vibiz ai "Name a bakery in three words."
vibiz image "a croissant on a marble table" -o hero.png
vibiz say "Welcome to Acme Bakery" -o welcome.mp3 --voice alloy
vibiz transcribe call.m4a --language en
```

Agents use `vibiz_ai_chat`, `vibiz_image_generate`, `vibiz_speech_create` and `vibiz_speech_transcribe`; media comes back as links, never base64; with a project the link is permanent and the file is in the project's Files (`vibiz_files`). The same as actions: `POST /v1/actions/ai.chat { "input": { "prompt": "…" } }`.

| Route | Does |
| --- | --- |
| `POST /v1/ai/chat/completions` | chat, streaming (SSE) or not; tools, JSON mode |
| `POST /v1/ai/embeddings` | embeddings |
| `POST /v1/ai/images/generations` | images: `b64_json` (default) or `url` |
| `POST /v1/ai/audio/speech` | text to speech, streamed `mp3` or `pcm` |
| `POST /v1/ai/audio/transcriptions` | speech to text, multipart `file` |
| `POST /v1/ai/jobs`, `GET /v1/ai/jobs/:id` | slow images and video: `202`, then poll |
| `GET /v1/ai/models`, `GET /v1/ai/health` | catalogue with prices; per capability `ok`/`degraded`/`down` |

Voices are `alloy`, `ash`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`. Pass your own user's id as `user` (or `X-Vibiz-User`) and spend breaks down per end user.

## Generated media is kept in Files

Every image, video and audio you generate is saved in the project's [Files](https://app.vibiz.app/docs/files), with the model, the prompt and the cost. Use the `url` in the response (it is permanent), or `vibiz files latest` / `vibiz files search "<prompt words>"` later.

Files are named by date and prompt (`2026-10-28-cold-email-cover.png`). A call with a development key (the development app key or AI key) saves to the development Files; every other project call saves to production. A call billed to the account with no project has no Files to go to: it is not saved and its links expire after 24 hours. Saving never fails the call: if it can't be saved, you get the 24-hour link and no file fields.

The answers keep their OpenAI shape and add:

| Call | Added |
| --- | --- |
| `POST /v1/ai/images/generations` | `file_id` and `file_url` on each `data[i]`; with `response_format: "url"`, `url` is the permanent link |
| `POST /v1/ai/jobs` (image and video), once done | `file_id` and `file_url` on each `output.data[i]`; `url` is permanent and `expires_at` is `null` |
| `POST /v1/ai/audio/speech` | headers `X-Vibiz-File-Id` and `X-Vibiz-File-Url` (the file appears once the audio has finished) |
| actions `image.generate`, `speech.create` | `url` permanent, plus `file_id` and `file_url` |

## Your own keys

Have Google Cloud credits, or a Google account you'd rather pay? Add your own Google key and **Google's models** run on **your** Google account: Google bills you, and the call costs **0** on Vibiz (each answer says `"paid_by": "your_key"`, with `would_cost_micro_usd`: what it would have cost here). Models from other labs keep running on Vibiz, billed as usual: your key never moves a Vibiz model to a Gemini model.

Two kinds of Google credentials:

| Type | What it is | Serves |
| --- | --- | --- |
| `"gemini_api_key"` | a Gemini API key from Google AI Studio | Google's chat, image, video, speech and transcription models, and embeddings if you turn them on |
| `"vertex"` | a Google Cloud service account key file, a project id and a region (default `global`): Vertex AI, which spends Google Cloud credits | Gemini 3.1 Flash Lite, 3.5 Flash Lite and 3.5 Flash chat, and transcription, today; the rest stays on Vibiz |

```bash
GEMINI_API_KEY=… vibiz ai keys set google          # or type it at the hidden prompt
vibiz ai keys set google --vertex sa.json --region us-central1
vibiz ai keys                                       # status, and which models run on your key
vibiz ai keys test google
vibiz ai keys remove google
```

Or in the console: **API keys ▸ Your AI keys**. With the API: `PUT /v1/ai-keys/google` `{ "type": "gemini_api_key", "api_key": "…" }` (owner keys only). A key is checked with a free call to Google before it is saved, and refused with the reason if Google refuses it. It is stored encrypted and never shown again: only its last 4 characters, when it was last used and its last error. A project can have its own key (`/v1/projects/:id/ai-keys/google`), which replaces the account's for that project.

What to know:

- **No silent fallback.** A call your key can serve runs only on your key. If Google refuses it or is down, the call fails (`provider_failed`, `details.reason: "customer_key_rejected"`, or `provider_unavailable`): it is never retried on Vibiz's account, so you are never billed by us by surprise.
- **What runs on your key**: any `google/…` model by its id (`google/gemini-4-argon`, `google/gemini-3.8-flash-tts`, `google/gemini-omni-1.1-flash`, `google/gemini-embedding-2`…), and the Vibiz models whose model is Google's: `vibiz/balanced` (Gemini 3.8 Flash), `vibiz/image` (Nano Banana 2) and `vibiz/image-pro` (Nano Banana 2.1).
- **Everything else runs on Vibiz**, billed as usual: the key's status lists every Vibiz model with `paid_by: "your_key"` or `"vibiz"`.
- **Embeddings are off by default** (`use_for`): vectors from another model don't match the ones your index already holds, and nothing would error. Turn them on for a new index: `PATCH /v1/ai-keys/google { "use_for": ["llm", "embeddings", …] }`.
- **Speech on your Google key comes back as WAV** (`audio/wav`) when you ask for `mp3`: we don't convert audio. `pcm` is the same 24 kHz 16-bit mono PCM as always.
- **Transcription on your Google key** takes files up to 14 MB, and video takes `16:9` or `9:16`.
- A job made on your key fails if you remove the key before it runs; it never runs on ours.

## What agents can and can't do

Agents may call every AI route and tool; each call is metered and held against the daily ceiling. Rotating the project's production AI key is an owner's (`POST /v1/projects/:id/ai/keys/rotate`), and so are setting and removing your own provider keys; agents can read their status (`vibiz_ai_keys`), never a secret.

## Errors

OpenAI-shaped, so OpenAI SDKs read them: `{ "error": { "code", "message", "type", "param", "details"? } }`.

| Code | Status | When | Next step |
| --- | --- | --- | --- |
| `invalid_request` | 400 | bad input, unknown model, a cap exceeded; `param` names the field | fix the field |
| `content_filtered` | 400 | a content filter refused the request | change the prompt |
| `context_length_exceeded` | 400 | the input is longer than the model's window | shorten it |
| `payload_too_large` | 413 | body or upload over its cap | send less |
| `unauthorized` | 401 | missing, invalid or revoked key | check the key |
| `rate_limited` | 429 | `details.reason`: `daily_spend`, `project_budget`, `end_user_budget`, `requests_per_minute`, `tokens_per_minute`, `concurrency` | wait (`Retry-After`), or raise the budget; `daily_spend` resets at midnight UTC |
| `provider_unavailable` | 503 | every route failed; `details.reason` `timeout`, `stall`, `circuit_open` | retry later |
| `model_unavailable` | 503 | no route can run that model right now; Vibiz never swaps in another | retry after `details.retry_after_seconds`, or pick another model |
| `not_configured` | 503 | this model can't be served right now | use another Vibiz model |

Mid-stream, the error arrives as a final SSE event, `data: {"error": {…}}`, without `[DONE]`.
