Docs / Inference API

[ REFERENCE ]

Inference API

One endpoint, one token, one balance. Point anything that speaks the OpenAI chat-completions protocol at Oddysey and it works — no account with a model provider, no second key to rotate, and every call on the same ledger as the rest of your deck.

What this is

Oddysey holds the upstream model account. You hold a deck token. The spend lands on your credit balance — the same one the MCP server draws down — and shows up in the same ledger.

The endpoint is POST /api/v1/chat/completions and the base URL is https://oddysey.dev/api/v1. It speaks the OpenAI chat-completions shape: an SDK, a curl loop, or a chat client with a custom base URL works by changing two strings.

  • Supported: system, user and assistant messages, temperature, top_p, max_tokens, stop, stream, and response_format: { type: "json_object" }.
  • Not supported: tool calling, images, logprobs, and the legacy completions endpoint. A request that uses them is rejected with a message that says so rather than being quietly stripped.

Authentication

Send a deck token as a bearer token. Issue one in the Command Deck under Settings → MCP; the same token works for the MCP server and for inference, and revoking it stops both at once.

Your first callBASH
curl https://oddysey.dev/api/v1/chat/completions \
  -H "Authorization: Bearer $ODDYSEY_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "model": "deepseek/deepseek-v4-flash",
    "messages": [
      { "role": "system", "content": "You review watch plans. Be brief." },
      { "role": "user", "content": "Is a -5% threshold sensible for NVDA?" }
    ]
  }'

Models and prices

Every id here is the provider's own: a call you already have working elsewhere keeps its model string. Prices are Oddysey list prices per million tokens and are what your balance is charged — the routing margin is already in them.

GET /api/v1/models returns this same table as JSON

MODELBYCONTEXTIN / 1MOUT / 1MALIAS
anthropic/claude-sonnet-5Anthropic1M$2.50$12.50oddysey-deep
openai/gpt-5.2OpenAI400k$2.20$17.50
google/gemini-3.7-flashGoogle1M$0.47$2.35
deepseek/deepseek-v4-flashDeepSeek1M$0.11$0.21oddysey-fast
anthropic/claude-haiku-4.5Anthropic200k$1.25$6.25oddysey-balanced
x-ai/grok-4.6xAI500k$2.50$7.50
openai/gpt-5-miniOpenAI400k$0.32$2.50
qwen/qwen3-maxQwen262k$0.98$4.88
meta-llama/llama-4-maverickMeta1M$0.25$1.00oddysey-open
mistralai/mistral-large-2512Mistral262k$0.63$1.88
cognitivecomputations/dolphin-mistral-24b-venice-editionVenice128k$0.25$1.13

The catalogue with logos, tiers and a copy button per id lives at oddysey.dev/inference.

Using an existing SDK

Nothing here is Oddysey-specific except the base URL and the key, so the official OpenAI clients work unmodified for the subset above.

TypeScriptTS
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://oddysey.dev/api/v1",
  apiKey: process.env.ODDYSEY_TOKEN,   // odsy_… from Settings → MCP
});

const answer = await client.chat.completions.create({
  model: "anthropic/claude-haiku-4.5",
  messages: [{ role: "user", content: "Summarise this ledger." }],
});

Streaming

Set stream: true and the response is server-sent events in the usual shape. Oddysey re-emits every frame rather than forwarding it: the model field is your alias, and the last frame carries the usage the call is billed on.

StreamingTS
const response = await fetch("https://oddysey.dev/api/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ODDYSEY_TOKEN}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    model: "oddysey-fast",       // a house alias works everywhere an id does
    stream: true,
    messages: [{ role: "user", content: "Explain a watch cooldown." }],
  }),
});

// Standard OpenAI SSE frames: data: {...}\n\n, terminated by data: [DONE].
// The final frame carries "usage" — that is what the call is billed on.

What a call costs

Money is held before the model runs and reconciled after it finishes. The hold is an estimate from your prompt size and your max_tokens; the settlement uses the token counts the provider reports, and the difference comes back as a refund entry in your ledger. A buffered response tells you both figures, in USDC atomic units — 1,000,000 to the dollar, the same unit the ledger moves.

Response bodyJSON
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "model": "deepseek/deepseek-v4-flash",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "…" }, "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 412, "completion_tokens": 231, "total_tokens": 643 },
  "oddysey": {
    "cost": 91,
    "credits_remaining": 49909
  }
}
  • x-oddysey-credits — atomic USDC units left after this call, on both streaming and buffered responses.
  • x-oddysey-model — the alias that served the request.

Running out of credit

When the balance cannot cover the hold, the request comes back as a 402 Payment Required carrying x402 payment requirements — the same protocol the MCP endpoint uses. An agent with a wallet can settle it and retry the identical request. A person can top up in the deck instead.

Errors

STATUSMEANS
401No bearer token, or one that is unknown or revoked.
400The body did not validate, or the model id is not in the catalogue.
402Out of credit. The body carries what to pay and where.
429The upstream provider is rate limiting. Retry shortly.
502The provider refused or returned something unreadable. Not your balance, not your token.
503Inference is not configured on this deployment, or the provider is down.

Error bodies are in the OpenAI shape — { error: { message, type, code } } — because the clients pointed at this endpoint already know how to read that.