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
- Sign in with email or a wallet.
- Add funds on the Billing page with USDG on Robinhood Chain.
- Create a key on the API keys page. It is shown once, so store it in a secret manager.
- Pick a model id from Models or
GET /v1/models.
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."}]
}'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)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
| Endpoint | Purpose |
|---|---|
| GET /v1/models | OpenAI-format model list with a stovra object holding prices and capabilities. Key optional. |
| POST /v1/chat/completions | OpenAI 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/messages | Anthropic Messages format for models marked "Messages API". |
| POST /v1/images/generations | Not 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.
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.
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
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
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request, max_tokens_too_large, context_length_exceeded | Fix the request body. |
| 401 | invalid_api_key, revoked_api_key, expired_api_key | Check the key. |
| 402 | insufficient_balance | Add funds or lower max_tokens. |
| 403 | insufficient_scope, model_not_allowed, spend_limit_reached, account_frozen | Key or account restriction. |
| 404 | model_not_found | The model is not in the live catalog. |
| 409 / 422 | idempotency_in_progress, idempotency_key_reused | Idempotency conflict. |
| 429 | rate_limit_exceeded, concurrency_limit, upstream_rate_limited | Slow down. Honor Retry-After. |
| 502 / 503 | upstream_error, upstream_no_capacity, model_temporarily_unavailable | Model request failed. You were not charged. |
Keys and limits
- Scopes:
inferencefor completions and messages,models:readfor 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-Keyon 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.