Skip to content
STOVRA

Documentation

Build with Stovra

Stovra exposes an OpenAI-compatible API at https://stovra.xyz/v1. Any OpenAI SDK works by changing the base URL and key.

Quickstart

  1. Sign in with email or a wallet.
  2. Add funds on the Billing page with USDG on Robinhood Chain.
  3. Create a key on the API keys page. It is shown once, so store it in a secret manager.
  4. Pick a model id from Models or GET /v1/models.
curl
export STOVRA_API_KEY="sk-stv-..."

curl https://stovra.xyz/v1/chat/completions \
  -H "Authorization: Bearer $STOVRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "max_tokens": 300,
    "messages": [{"role": "user", "content": "Write a haiku about ledgers."}]
  }'
Python (openai>=1.0)
import os
from openai import OpenAI

client = OpenAI(base_url="https://stovra.xyz/v1", api_key=os.environ["STOVRA_API_KEY"])

resp = client.chat.completions.create(
    model="MODEL_ID",
    max_tokens=300,
    messages=[{"role": "user", "content": "Write a haiku about ledgers."}],
)
print(resp.choices[0].message.content)
Node.js (openai v4+)
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://stovra.xyz/v1", apiKey: process.env.STOVRA_API_KEY });

const resp = await client.chat.completions.create({
  model: "MODEL_ID",
  max_tokens: 300,
  messages: [{ role: "user", content: "Write a haiku about ledgers." }],
});
console.log(resp.choices[0].message.content);

Authentication

Send your key as Authorization: Bearer sk-stv-.... The Messages endpoint also accepts x-api-key. Keys are stored only as keyed hashes, so a lost key cannot be recovered: revoke it and create a new one.

API reference

EndpointPurpose
GET /v1/modelsOpenAI-format model list with a stovra object holding prices and capabilities. Key optional.
POST /v1/chat/completionsOpenAI Chat Completions. Supports stream, tools, tool_choice, response_format, stop, temperature, top_p, seed, max_tokens or max_completion_tokens. n must be 1.
POST /v1/messagesAnthropic Messages format for models marked "Messages API".
POST /v1/images/generationsNot available yet. Returns 501.

If you omit max_tokens, the model's default output cap applies (shown in the catalog). Output is always bounded so the reservation is a true maximum.

Streaming

Set "stream": true to receive Server-Sent Events. Pass stream_options: {"include_usage": true} to get the final usage chunk, exactly as with OpenAI.

Python streaming
stream = client.chat.completions.create(
    model="MODEL_ID",
    stream=True,
    stream_options={"include_usage": True},
    messages=[{"role": "user", "content": "Count to five."}],
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

Tool calls

Tool definitions and tool call deltas pass through unchanged for models that support tools (see the Tools badge). Your code executes the tool and sends the result back as a tool message.

Node.js tool call
const resp = await client.chat.completions.create({
  model: "MODEL_ID",
  messages: [{ role: "user", content: "What's the weather in Jakarta?" }],
  tools: [{
    type: "function",
    function: {
      name: "get_weather",
      parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
    },
  }],
});
console.log(resp.choices[0].message.tool_calls);

Messages API

Python (anthropic SDK)
import os
import anthropic

client = anthropic.Anthropic(base_url="https://stovra.xyz", api_key=os.environ["STOVRA_API_KEY"])
msg = client.messages.create(
    model="CLAUDE_MODEL_ID",
    max_tokens=512,
    messages=[{"role": "user", "content": "Explain double-entry bookkeeping in two sentences."}],
)
print(msg.content[0].text)

Errors

StatusCodeMeaning
400invalid_request, max_tokens_too_large, context_length_exceededFix the request body.
401invalid_api_key, revoked_api_key, expired_api_keyCheck the key.
402insufficient_balanceAdd funds or lower max_tokens.
403insufficient_scope, model_not_allowed, spend_limit_reached, account_frozenKey or account restriction.
404model_not_foundThe model is not in the live catalog.
409 / 422idempotency_in_progress, idempotency_key_reusedIdempotency conflict.
429rate_limit_exceeded, concurrency_limit, upstream_rate_limitedSlow down. Honor Retry-After.
502 / 503upstream_error, upstream_no_capacity, model_temporarily_unavailableModel request failed. You were not charged.

Keys and limits

  • Scopes: inference for completions and messages, models:read for the model list.
  • Rate limit: requests per minute per key (default 60).
  • Concurrency: simultaneous in-flight requests per key (default 4).
  • Spend limit: optional daily, monthly, or lifetime cap per key. A request is refused if its reservation would cross the cap.
  • Model allowlist: optionally restrict a key to specific models.
  • Idempotency: send Idempotency-Key on non-streaming requests to safely retry. A repeated key with the same body replays the stored response without a second charge for 24 hours.

Billing and usage

Each response includes x-stovra-request-id. Non-streaming responses also include x-stovra-cost-usd, and x-stovra-usage-estimated: true when token usage was not reported. Every request appears in Usage and every balance movement in the ledger.

Deposits

  • Network: Robinhood Chain, chain ID 4663.
  • Send only from a wallet you linked, and send the exact amount shown for that deposit. The amount identifies your deposit.
  • Credits appear after 120 confirmations. Transfers that do not match a deposit are held for manual review, not lost.
  • Stock Token deposits require an eligible residence and location, and a fresh quote. See the disclosures.

Agents

Create agents in the app. Runs are queued and executed by an isolated worker with the tools you grant. Each run records its steps, output, and cost. Scheduled agents run at most every 15 minutes and pause if your balance cannot cover a run.