Pioneer Server API
OpenAI- and Anthropic-compatible inference relay with smart provider routing.
Built with Bun. Zero framework overhead.
Base URL
https://alpha.pioneers.devAll API paths below are relative to this base. The OpenAI-compatible root is
https://alpha.pioneers.dev/api/v1.
Authentication
Inference requires an API key. Send it either way:
Authorization: Bearer sk-pioneer-<64 hex chars> # OpenAI convention
x-api-key: sk-pioneer-<64 hex chars> # Anthropic SDK conventionGet a key at https://alpha.pioneers.dev/keys (sign in with your wallet).
Keys spend credits; fund your address at https://alpha.pioneers.dev/leaderboard.
Public, no-auth endpoints: GET /api/v1/chat/health, GET /health, GET /,
GET /metrics, GET /llms.txt, GET /docs, the leaderboard, and metrics.
Agent Handoff — wire Pioneers as a provider
Pioneers is OpenAI-compatible, so any agent runtime that accepts a custom
OpenAI base URL can use it. Given only a sk-pioneer-... key, an agent should:
1. Validate the key first (expect HTTP 200; do not edit config on non-200):
curl -sS -o /dev/null -w "%{http_code}" \
https://alpha.pioneers.dev/api/v1/chat/completions \
-H "Authorization: Bearer $PIONEERS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"ping"}]}'2. Configure the provider with:
- baseUrl: https://alpha.pioneers.dev/api/v1
- apiKey: the sk-pioneer-... key (from env PIONEERS_API_KEY, never on disk)
- api: openai-completions
- model id: auto (Pioneers routes to the best backend internally)
3. Verify with a one-shot completion asking the model to echo a sentinel
string, and confirm the sentinel appears in the response.
OpenClaw users: the full step-by-step installer (env, openclaw.json, auth
profiles, gateway restart) lives in the Pioneers Installer agent skill.
Chat Completions
POST /api/v1/chat/completions
OpenAI-compatible chat completions with smart routing across providers.
Auth + credits required. Supports streaming (SSE), tool calling, and
reasoning/thinking token passthrough.
Request:
{
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hello" }
],
"model": "auto",
"max_tokens": 1000,
"temperature": 0.7,
"stream": false,
"chat_template_kwargs": { "enable_thinking": false },
"tools": [],
"tool_choice": "auto"
}Response (non-streaming):
{
"id": "chatcmpl-...",
"object": "chat.completion",
"model": "glm-5.3-flash-local",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help?",
"reasoning_content": "The user said hello..."
},
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 12, "completion_tokens": 8, "total_tokens": 20 }
}Response (streaming): Server-Sent Events (SSE)
data: {"object":"chat.completion.chunk","choices":[{"delta":{"reasoning_content":"thinking..."}}]}data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello!"}}]}
data: [DONE]
Model capacity: discover deployed capacity through authenticated
GET /api/v1/models before inference. Never infer capacity from a model name.Model Resolution:
"auto"/omitted, pioneer, pioneer-primary,
pioneer-default, and glm-5.3-flash select the first configureddefault. The
auto metadata record names its concrete resolved_model.Capacity-aware clients should refresh discovery and request that concrete model.
Concrete names (including
gpt-*) are honored exactly or rejected beforegeneration. Each request is pinned to one route, with no inference fallback.
Thinking/Reasoning: thinking is OFF by default on the primary. Enable per
request with
"chat_template_kwargs": {"enable_thinking": true} or
"reasoning_effort": "high". Reasoning is returned as message.reasoning_content(and
delta.reasoning_content when streaming) regardless of backend; thebackend's
reasoning field is mirrored to it.Tool calling: use native OpenAI
tools[]/tool_calls[] (JSON-string
arguments, finish_reason: "tool_calls"). Tool calls described only in theprompt (
<tool_call>{json}</tool_call> in content) are NOT returned in
content by the primary — migrate to tools[].Accepted request fields:
model, messages, stream, tools, tool_choice,
parallel_tool_calls, temperature, top_p, max_tokens, max_completion_tokens,
frequency_penalty, presence_penalty, stop, seed, n, logprobs, top_logprobs,
response_format, reasoning_effort, chat_template_kwargs. Others are dropped.Output budget: use one of
max_tokens or max_completion_tokens, a positiveinteger no greater than the route's advertised
max_output_tokens (includingreasoning). When omitted, the relay applies that allowance. The relay ceiling
is configured with
PIONEER_MAX_OUTPUT_TOKENS (default 16384); lower backendallowances and context capacities take precedence.
Errors: inference errors use
{error:{message,type,code}}. Context overflowreturns HTTP 400 before streaming starts, with
code: context_length_exceeded and
type: invalid_request_error; history is never trimmed or replayed. Missingcapacity returns 503; unavailable concrete models return 400. SSE interruption
or missing terminal finish reasons emit
error.code: incomplete_stream andclose without
[DONE]. Finish reasons and usage are preserved, including
length, which denotes a partial response. Caller cancellation aborts upstream.Streaming liveness: chat/code requests send SSE comment heartbeats every five
seconds during discovery, upstream header waits and quiet generation intervals.
After receiving the request body, streaming inference has a ten-minute absolute
deadline; heartbeats do not extend it. A late failure after HTTP 200 is an SSE
error retaining its code and original status. Deadline failures use
inference_timeout. Errors never fabricate successful [DONE] or replay inference.Use
x-request-id to correlate response headers, JSON/SSE errors and the upstreamrequest. Stream failures include
retryable: false; do not automatically replayaccepted generation just because the connection was interrupted.
Machine-readable contract: GET /openapi.json.
Pioneer Analytics
POST /api/v1/analytic
Synchronous typed decisions from a situation and named questions. Requires the
same Pioneer API key or wallet JWT as inference and spends the owner's credits.
Optional inline PNG, JPEG and WebP images support the same choice, score and noul
questions. This endpoint does not generate captions, free-form text or images.
Videos and media URLs are unavailable.
{
"model": "analytic-latest",
"state": {"task": "calculate a total from a spreadsheet"},
"questions": {
"tool": {"type": "choice", "instructions": "Which tool fits?",
"criteria": {"browser": "a web browser", "calculator": "a calculator"}},
"risk": {"type": "score", "instructions": "How destructive is this?",
"criteria": ["harmless", "minor", "moderate", "severe"]},
"ask": {"type": "noul", "instructions": "Should the user confirm first?"}
}
}state accepts non-empty text, an object, or an array; it is always treated astext. Nested media-looking objects or URLs are never fetched. Optional image input
goes in
images: [{"mime_type":"image/png","data":"<canonical base64 bytes>"}].Use
image/png, image/jpeg or image/webp, with exactly mime_type and data.Do not use a data URL, file path, remote URL, video or processing override.
Images must be single-frame, with 1–2 images per request. Each is at most 2 MiB
decoded; combined at most 4 MiB. Source width and height are 1–4096 pixels each,
with at most 4,194,304 pixels per image and aspect ratio at most 200:1. The trusted backend fully decodes and
checks format and pixels, then processes at most 262,144 pixels per image.
Clients cannot raise that processing cap. EXIF orientation is respected and
transparency is composited on white before processing.
choice requires a
non-empty object of option names to descriptions. score accepts a non-empty
list or object of ordinal descriptions. noul takes no criteria and returns the
probability of yes. Every question requires instructions.
Responses contain model, answers keyed by question ID, and
usage: {input_tokens, output_tokens: 0}. Choice answers include choice,
confidence, and probabilities; score answers include expected score,
confidence, legend, and probabilities. These probabilities are estimates;
they do not grant tool permissions or replace owner approval.
The public model alias is analytic-latest; callers cannot select a private
backend. Text-only bodies and the request excluding images have a 64 KiB limit. Image
JSON bodies have a 6 MiB limit. Requests allow 32 questions, 64 state entries, and
64 criteria per question. Instructions are at most 4,000 characters and criteria
descriptions at most 1,000. The configured context ceiling is 16,384 input tokens.
Billing: max(1, ceil(input_tokens / tokens_per_credit)); the current default
is 10,000 input tokens per credit. Native visual, text and question tokens all
count toward input usage and the unchanged 16,384-token total context ceiling.
After accounting for images and questions, the encoder may trim state text to
fit the remaining context. Returned input_tokens counts the effective encoded
input, so not every uploaded character is necessarily evaluated.
Before inference, the service reserves enough
credits for the context ceiling (2 credits at that rate). A smaller request
refunds the unused reservation; failures refund the complete reservation.
x-pioneer-credits-charged reports the actual cost and
x-pioneer-input-tokens-per-credit reports the price used. No output tokens are
generated or billed. Pricing is unchanged during this validation release.
Limits: per authenticated owner, burst 10, sustained 60 requests/minute, and
2 concurrent calls. Each server process allows 16 concurrent calls. Traffic and
upstream queue rejection return 429 with Retry-After; insufficient reservation
credits also return 429, with credits_required. Retry only after the indicated
delay; an ordinary inference request is not an idempotent purchase operation.
Errors: 400 invalid JSON, 413 oversized body/image/dimensions, 422 invalid schema/image/animation, 429 busy or
unfunded, 502 invalid/unreachable upstream, 503 unconfigured/unavailable service,
and 504 timeout. The service timeout is at most 65 seconds. No credits are charged
for these failed calls. The OpenAPI definition is published at /openapi.json.
Hosted Pioneer agents use their metered local Beast inference gateway for
Analytics and GLM. They do not round-trip through this public paid endpoint.
POST /api/v1/code/chat/completions
Same OpenAI spec as /chat/completions, but injects Pioneer's coding-agent
system prompt before relaying. Auth + credits required.
GET /api/v1/chat/models
List available models across configured providers. Auth required. Responses
are not cached. Records retain max_model_len and expose normalized capacity
when the backend provides it. auto is omitted if its capacity is unknown.
(/api/v1/models and /api/v1/code/models are aliases.)
{
"object": "list",
"data": [
{ "id": "auto", "resolved_model": "glm-5.3-flash-local", "context_window": 524288, "max_output_tokens": 16384 },
{ "id": "glm-5.3-flash-local", "resolved_model": "glm-5.3-flash-local", "provider": "vllm", "max_model_len": 524288, "context_window": 524288, "max_output_tokens": 16384 }
]
}GET /api/v1/chat/health
Provider health status (public).
{ "status": "ok", "name": "pioneer-server", "version": "2.0.0", "uptime": 3600.5 }Speech & Lore (auth + credits required)
All take Authorization: Bearer <jwt|sk-pioneer-*>; 429 when out of credits.
| Method | Path | Body → Response |
| POST | /api/v1/tts | {text, voice_description?, voice_id?, control?} → audio/wav (48 kHz mono). control is a free-text delivery direction ("whispering, afraid", ≤1000 chars). cfg_value/inference_timesteps are fixed upstream and ignored if sent |
| POST | /api/v1/tts/stream | same → chunked raw PCM16LE (X-Sample-Rate, X-Channels, X-Sample-Format headers) |
| POST | /api/v1/voice | mint a persistent voice_id from a reference clip or designed voice |
| POST | /api/v1/stt | multipart audio, timestamps? → {text} |
| POST | /api/v1/lore | {question, context?} → {answer, sources?} (small non-reasoning model) |
| POST | /api/v1/performance | {text, voice_id?, voice_description?, performance?, includeFaceAnimation?} → JSON: the spoken line plus facial animation curves on one timeline |
| POST | /api/v1/face/generate | multipart audio, emotion?, include_raw? → JSON facial animation curves for audio you already have |
| GET | /api/v1/face/channels | the 52 channel names, and which are unsupported |
| GET | /api/v1/face/health | face model, revision and solver version |
Directing delivery: control
control changes how a line is spoken without changing the voice. A voice clonedwith a transcript cannot take a direction:
/api/v1/tts answers 400 for
control with such a voice_id. /api/v1/performance keeps the voice, drops thedirection and reports it in
performance.tts_note.POST /api/v1/performance
One call returns the audio and the face that speaks it. Whole clip, no streaming;
use /api/v1/tts/stream when time to first sound matters more than a face.
{
"text": "I'll hold the rope while you climb.",
"voice_id": "v_...",
"performance": { "emotion": "concerned", "intensity": 0.7 },
"includeFaceAnimation": true
}- voice_id or voice_description, as for /api/v1/tts.
- performance.emotion: neutral (default), happy, excited, sad, grieving,
angry, afraid, concerned, surprised, disgusted, playful, tired,
pained. An unknown emotion is a 400 that lists the valid ones.
- performance.intensity: 0 to 1, default 1.
- includeFaceAnimation: default true; false omits face.
{
"audio": { "format": "wav", "base64": "..." },
"performance": {
"emotion": "concerned", "intensity": 0.7,
"tts_control": "very concerned, worried, gentle",
"tts_note": null,
"face_emotion": { "fear": 0.35, "sadness": 0.28 }
},
"face": {
"model": { "name": "audio2face-3d-v2.3.1-claire", "revision": "..." },
"solver": { "name": "pca-box-qp", "version": "0.1.0", "parity": "candidate; ..." },
"audio": { "sha256": "...", "duration_s": 1.92, "sample_rate": 16000 },
"fps": 30, "frame_count": 58,
"channels": ["eyeBlinkLeft", "..."],
"unsupported_channels": ["tongueOut"],
"times": [0.0, 0.0333],
"weights": [[0.0, 0.12]]
}
}- audio.base64 is the 48 kHz mono WAV, about 130 KB of base64 per second.
- face.weights[frame][channel] is 0 to 1; channel order is face.channels
(ARKit names). Frame k plays at k / 30 seconds of the returned audio.
- The curves are for one neutral face. Mapping channels to a character's rig, and
any per-character strength, is the client's job.
- Store face.solver.name and version with cached curves. The solver is a
candidate, not yet certified against NVIDIA's reference, and curves should be
regenerated when it changes. Cache key: face.audio.sha256 + solver version +
emotion.
- tts_note is set when the delivery direction was dropped; the face emotion
still applies.
POST /api/v1/face/generate
Curves for audio you already have: multipart audio (WAV or FLAC, up to 120 s),
optional emotion as a JSON object over amazement, anger, cheekiness,
disgust, fear, grief, joy, outofbreath, pain, sadness (each 0 to 1),
optional include_raw. The response is the face object above.
Errors
400 bad input, including an unknown voice_id. 502 the speech or face backendfailed.
503 the face backend is not available. Backend errorscarry their message in
detail; errors raised by this server use error.Anthropic-Compatible Messages
POST /v1/messages (alias: POST /api/v1/messages)
Anthropic Messages API shape, translated to the relay. Auth via x-api-key
or Authorization: Bearer. GET /v1/models returns an Anthropic-shaped model
list (used by the Claude Code SDK to probe the server).
OpenAI Responses (Codex-compatible)
POST /v1/responses (alias: POST /api/v1/responses)
OpenAI Responses request, response, SSE, and structured function-call shapes,
translated onto Alpha's inference router. Auth uses the same wallet-bound JWT
or sk-pioneer-* API key as /v1/messages. model: "auto" selects Alpha's
default provider route. The initial streaming implementation buffers upstream
generation, then emits a complete Responses event lifecycle.
Use https://alpha.pioneers.dev/v1 as the base URL for a Codex custom provider
with wire_api = "responses". Keep the Alpha credential process-local; do not
replace a user's native Codex/OpenAI login configuration.
Meet (wallet-authenticated rooms)
Signed-wallet JWT only (Authorization: Bearer <jwt> from /auth/verify; sk-pioneer-* keys get 401).
Each wallet may send 600 meeting requests a minute (429 after that).
| Method | Path | Description |
| POST | /api/v1/meetings | { title?, company? } → 201 { id }. No company = standalone room (any signed wallet); with company the caller must be a member. A nonmember of an existing company gets 403 { error: "Company approval required", pending: true, company } and a join request is filed for the owner (no room is created); an unknown company is 403 Company member required |
| GET | /api/v1/meetings?company={address} | Active rooms of a company, oldest first (members only) |
| GET | /api/v1/meetings/companies | { companies: [{ address, name }] } the caller belongs to |
| POST | /api/v1/meetings/{id} | Room command { peerId, type, ... }; type is join (adds iceServers), poll (after = signal cursor), leave, signal (to, data), chat (text), media (camera, screen), recording (enabled, company members), end |
Rules: six human seats, three cameras, one screen share; the Office recorder takes no seat. Guests join any
room by its id. Company rooms end after 60 minutes without a human member; standalone rooms end one hour
after they start, have no recording, and only their organizer may end them early. Errors: 404 unknown room, 409 limits, 410 ended,
503
Meeting busy; retry. Rooms idle for seven days are deleted.Companies
A company is identified by its owner's wallet address (one per wallet). Addresses are matched in any casing and
returned lowercase, except owner, which is returned as given. JWT or API key unless marked public.
| Method | Path | Description |
| GET | /companies/create | Public page: connect a wallet, burn LP, create the company |
| POST | /api/v1/companies/create | { name, txHash } (a CreditShop burn whose recipient is the caller) → { ok, companyAddress, slug, plan, burn }; the owner seat is funded once; 409 if you already own a company |
| POST | /api/v1/companies/create-test | { name } → named test company, no credits; 409 if already named |
| POST | /api/v1/companies/join | { companyAddress, name?, memberType?, capabilities? } → created (first joiner owns a nameless company), pending (owner approval needed) or already_member |
| POST | /api/v1/companies/leave | { companyAddress } |
| GET | /api/v1/companies/leaderboard | Public directory of named companies, most members first |
| GET | /api/v1/companies/{addr}/info | Public record + member count |
| GET | /api/v1/companies/{addr}/members | Members, owner first (members only) |
| GET | /api/v1/companies/{addr}/status | Members + live connections (members only) |
| GET | /api/v1/companies/{addr}/pending | Owner only: { companyAddress, requests: [{ address, name, memberType, capabilities, requestedAt }] }, oldest first |
| POST | /api/v1/companies/{addr}/approve | Owner only: { address } → { approved: true, member }; exactly once (404 afterwards); funds a plan seat while seats remain |
| POST | /api/v1/companies/{addr}/reject | Owner only: { address } → { rejected: true } |
| POST | /api/v1/companies/{addr}/settings | Owner only: { openInvite: boolean } |
| GET | /api/v1/account/companies | Every company the caller belongs to, with role and joinedAt |
Atlas (Assets & Networks)
| Method | Path | Description |
| GET | /api/v1/atlas/assets/{limit}/{skip} | Paginated crypto assets |
| GET | /api/v1/atlas/network/{chainId} | RPC nodes by EVM chain ID |
| GET | /api/v1/atlas/networks/{start}/{stop}/{limit} | Nodes across major EVM networks |
Apps (Dapp Store)
| Method | Path | Description |
| GET | /api/v1/apps/spotlight | Featured dapp (or null) |
| GET | /api/v1/apps/version/{minVersion}/{limit}/{skip} | Dapps by min client version |
| GET | /api/v1/apps/version/{version}/asset/{asset}/{limit}/{skip} | Dapps by asset + version |
Health & Server Info
| Method | Path | Description |
| GET | /health | Kubernetes health check → { "status": "ok" } |
| GET | / | Server metadata and status |
| GET | /metrics | Live metrics dashboard (SSE) |
Technical Details
- Runtime: Bun (Bun.serve, hand-rolled router — no framework)
- Auth: wallet challenge → JWT, or sk-pioneer-* API keys
- Provider chain: vLLM → Venice → OpenAI (discovery priority; inference is pinned)
- Streaming: SSE with reasoning_content (normalized from backend reasoning)
- Database: PostgreSQL (PlanetScale) for accounts, credits, keys, usage, purchases, companies, meeting rooms and hourly metrics; process memory for login challenges, meeting rate limits and live metrics
- Docs: this page (/docs) and /llms.txt
Qwen3 4B text chat
Explicit model qwen3-4b-instruct-2507 selects the dedicated Qwen upstream on
POST /api/v1/chat/completions. The primary and auto defaults remain unchanged.
Qwen has an 8192-token total context, including server chat formatting and reserved
output; the output default is 512 tokens. Alpha uses the running model's native
chat token-count endpoint before generation and returns context_length_exceeded
when prompt plus output cannot fit. max_completion_tokens is accepted as an
alternative to max_tokens. History is preserved; requests are never replayed or
sent to another model. Text messages (system/user/assistant) only; tools, structured
output, multimodal input and template overrides are unsupported.
Both JSON and SSE are supported, including backend usage and successful [DONE].
Qwen has one generation slot and one queued request, a 30-second queue wait limit,
and a two-minute total deadline. Queue saturation returns 429; unavailability
returns qwen_unavailable. Cancellation aborts upstream work and removes waiters.
The authenticated model catalog reports context, default_output_tokens,
max_concurrent_requests and supported capabilities. Existing authentication,
account/credit checks, rate limits and usage accounting apply.
Operators: QWEN3_4B_BASE_URL=https://inference.pioneers.dev/qwen3-4b/v1 and
QWEN3_4B_MODEL=qwen3-4b-instruct-2507. Existing CF Access credentials are attached
server-side only. The tunnel must route chat/completions, models, and
chat/completions/input_tokens under that prefix. No Alpha token-count proxy or
llama.cpp admin endpoints are exposed. Lore is configured separately.
Qwen3 1.7B
Explicit model qwen3-1.7b uses a separate GPU1 upstream. It has the same 8192
formatted total-token budget, 512 default output, JSON/SSE/usage support and
text-only restrictions as Qwen4B. Thinking is disabled server-side for both native
token admission and generation; template overrides are rejected. Each Qwen model
has its own one-slot, one-waiter queue. GLM remains the auto/default model.
Operators: QWEN3_1_7B_BASE_URL=https://inference.pioneers.dev/qwen3-1.7b/v1 and
QWEN3_1_7B_MODEL=qwen3-1.7b, using the existing server-side CF Access credentials.