tokennet
tokennet serves open models from people's own hardware, behind one OpenAI-compatible API. This is that API: sign in for a key and every command below runs as shown.
Quick start
Sign in, then create a key on the Keys tab, then paste these two commands. Nothing else to install — this is the whole API.
export TOKENNET_API_KEY=sk-tnk-...
curl https://data.tokennet.dev/v1/chat/completions \
-H "Authorization: Bearer $TOKENNET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "any", "messages": [{"role": "user", "content": "Say hello in five words."}]}'What comes back:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"model": "gemma-3-12b-it",
"maker": "someone's-workstation",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello there, nice to meet you."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 14, "completion_tokens": 8, "total_tokens": 22}
}Standard OpenAI fields, plus maker — the relay writes that itself, after validating the reply, so it always names whose machine actually answered.
The API
Three OpenAI-compatible endpoints — list what's being served, chat with it, and transcribe audio — and one for decisions. Anything that speaks the OpenAI chat API speaks this one: point its base URL here and give it a tokennet key.
| Endpoint | What it does |
|---|---|
GET /v1/models | Every model the fleet is serving right now. |
POST /v1/chat/completions | A chat completion, streaming or not. |
POST /v1/audio/transcriptions | The transcript of an audio file, streaming or not. Below. |
POST /v1/systemone | A decision: typed questions about some input, answered with probabilities by a decision model. Below. |
Base URL for this deployment:
https://data.tokennet.dev/v1Authenticate
Every request carries an API key as a bearer token. Sign in, then create a key on the Keys tab — it's shown once, so copy it then.
export TOKENNET_API_KEY=sk-tnk-...A bad or revoked key is 401. When no maker has capacity for your request the relay answers 503 — retry shortly rather than treating it as a failure.
List models
One entry per (maker, model) pair, plus the routing names. Each entry carries available and the maker serving it, so you can pick a specific maker or let the relay choose.
curl https://data.tokennet.dev/v1/models -H "Authorization: Bearer $TOKENNET_API_KEY"{
"object": "list",
"data": [
{"id": "any", "tokennet_pseudo": true, "available": true},
{"id": "gemma-3-12b-it", "available": true, "maker": "someone's-workstation"},
{"id": "qwen3-30b-a3b", "available": false, "maker": "a-sleeping-laptop"}
]
}The any route sends a request to whichever maker is free — it is the right default unless you need one particular model. The Models tab lists the same catalog with context windows and tool/thinking support.
Chat completions
The same request body OpenAI takes:
curl https://data.tokennet.dev/v1/chat/completions \
-H "Authorization: Bearer $TOKENNET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "any", "messages": [{"role": "user", "content": "Hello"}]}'Add "stream": true for server-sent events: each frame carries choices[0].delta.content, and the relay adds a maker byline naming whose machine answered.
Leave temperature unset unless you have a reason to set it: each model is then sampled the way its publisher recommends. In particular, "temperature": 0 — what many coding tools send by default — can make a thinking model repeat itself until it runs out of tokens and returns no answer at all. If that happens the reply says so, and raising max_tokens will not fix it. The Models tab marks which models think.
Full request and response reference: OpenAI chat completions →
Transcriptions
Upload a WAV, MP3 or FLAC file and get its text back. Name a speech-recognition model — the Models tab marks them — since any only ever routes to chat models.
curl https://data.tokennet.dev/v1/audio/transcriptions \
-H "Authorization: Bearer $TOKENNET_API_KEY" \
-F file=@meeting.mp3 \
-F model=qwen3-asr-0.6bWhat comes back:
{
"type": "transcript.text.done",
"text": "And so, my fellow Americans, ask not what your country can do for you...",
"maker": "someone's-workstation",
"usage": {"type": "tokens", "input_tokens": 171, "output_tokens": 30, "total_tokens": 201}
}OpenAI's fields, plus maker, and a type theirs leaves off the non-streamed reply — it is the same event a stream ends on.
Add -F stream=true for server-sent events: a transcript.text.delta per token, then one transcript.text.done carrying the whole text and the usage. language and temperature are passed to the model, but language makes it write in that language, translating if the audio is in another — set it only to the language spoken. response_format may only be json; prompt, timestamps and every other field are a 400, as is any other audio format. A file larger than this deployment accepts is a 413, and audio too long for the model's context is a 400 — split it and send the parts.
From the OpenAI Python client, set up as below:
text = client.audio.transcriptions.create(
model="qwen3-asr-0.6b", file=open("meeting.mp3", "rb"),
).textFull request and response reference: OpenAI transcriptions →
Decisions (System One)
For routing, guardrails and grading — anywhere the answer is a label or a yes/no, not text. A decision model answers every question in one pass and returns a probability for each option, so you can act on its confidence instead of parsing a chat reply. Name a model the Models tab marks as a decision model, or try one in the browser first.
curl https://data.tokennet.dev/v1/systemone \
-H "Authorization: Bearer $TOKENNET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "kev-4b",
"state": "Database replication lag on eu-west climbing since 02:10.",
"questions": {
"domain": {"type": "choice", "instructions": "Root-cause domain?",
"criteria": {"infrastructure": null, "billing": null}},
"page_now": {"type": "noul", "instructions": "Does this need a human tonight?"}}}'What comes back:
{
"model": "kev-4b",
"maker": "someone's-workstation",
"answers": {
"domain": {"type": "choice", "choice": "infrastructure",
"probabilities": {"infrastructure": 0.978, "billing": 0.022},
"confidence": 0.956},
"page_now": {"type": "noul", "noul": 0.461}
},
"usage": {"input_tokens": 61, "output_tokens": 0}
}This is TypeSafe's System One format, unchanged: a client written for Jev or any other System One provider works here by changing its base URL. A question is a choice (one of up to 255 options), a score (a 2–10 level rubric) or a noul (yes/no); a request asks 1–64 of them. There is no streaming — one JSON reply — and usage is input tokens only, since nothing is generated.
Full request and response reference: System One API →
From an OpenAI SDK
No tokennet library needed — override the base URL and the official clients work as-is.
Python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://data.tokennet.dev/v1",
api_key=os.environ["TOKENNET_API_KEY"],
)
# model="any" lets the relay pick a free maker.
reply = client.chat.completions.create(
model="any",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)TypeScript
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://data.tokennet.dev/v1",
apiKey: process.env.TOKENNET_API_KEY,
});
const reply = await client.chat.completions.create({
model: "any",
messages: [{ role: "user", content: "Hello" }],
});Route to your own makers
Name a subnetwork on the request and it goes nowhere else. Build one on the Subnetworks tab, then send its id as a header:
X-Tokennet-Subnetwork: <subnetwork-id>Working examples
The Apps tab lists the sample web apps running on this network. They use nothing but the two endpoints above, so what they do, you can do.