Back to the playground

Tenzro Infer — Docs

How to call the models served on the Tenzro network: access, endpoints, and the wire protocol. The API is OpenAI-compatible, so most existing clients work unchanged.

Access

Every model is gated by an API key. Requests without a valid key are rejected before any compute runs. Pass the key in the X-Tenzro-Api-Key header.

On this site the key is held server-side and never reaches the browser — the playground calls a same-origin proxy (/api/infer/*) that attaches the key and forwards to the node. Build your own integration the same way: keep the key on your server, never in client code.

Keys are minted per subject with a scope of inference, a requests-per-minute tier, and are read-only. Manage your own keys with tenzro key list-mine / tenzro key revoke-mine.

Endpoints

The node exposes an OpenAI-compatible surface under /v1:

EndpointMethodPurpose
/v1/chat/completionsPOSTChat (text, or text + images)
/v1/modelsGETList models the key may reach

Both models — qwen3.8-27b and muse-glimmer-30b — are multimodal: they take text and, optionally, one or more images, and return text. Through this site the same call is available same-origin at /api/infer/chat.

Protocol

The wire format is the OpenAI Chat Completions API. Any OpenAI SDK works — set the base URL to your node's /v1 and send the key in the header.

curl
curl https://infer.tenzro.com/api/infer/chat \
  -H "content-type: application/json" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{ "role": "user", "content": "Hello from Tenzro" }]
  }'
TypeScript (OpenAI SDK, streaming)
// The node speaks the OpenAI wire format, so any OpenAI client works.
// Point it at your node and pass the API key as X-Tenzro-Api-Key.
import OpenAI from "openai"

const client = new OpenAI({
  baseURL: "https://<your-node>/v1",
  apiKey: "unused",                       // key goes in the header below
  defaultHeaders: { "X-Tenzro-Api-Key": process.env.TENZRO_API_KEY },
})

const stream = await client.chat.completions.create({
  model: "qwen3.8-27b",
  messages: [{ role: "user", content: "Explain NVFP4 in one line." }],
  stream: true,
})

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "")
}

Streaming

Set stream: true for token-by-token Server-Sent Events. Each event is a data: line carrying a JSON delta; the stream ends with data: [DONE]. Streaming is the recommended default — it is the lowest-latency path and what the playground uses.

Provenance

Every response carries a tenzro_contentProvenance block: an ed25519 signature over the content hash, the model id, and the signing node's public key, asserting ai-generated. You can verify any output came from the model that claims it, unmodified — verifiable inference, not just a black-box call.

Multimodal

Attach images by sending an array of content parts — a text part and one or more image_url parts (a data: URL or an https URL). Both models read them; the response is text.

curl — text + image
# Multimodal: send text + an image (data URL or https URL) in one message.
curl https://infer.tenzro.com/api/infer/chat \
  -H "content-type: application/json" \
  -d '{
    "model": "muse-glimmer-30b",
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "What is in this image?" },
        { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
      ]
    }]
  }'

Limits

Free-tier keys are rate-limited (60 requests/minute by default). Usage is metered per token in TNZO on paid tiers; the playground runs on a gated demo key.