Pioneer Server API


OpenAI- and Anthropic-compatible inference relay with smart provider routing.
Built with Bun. Zero framework overhead.

Base URL


https://alpha.pioneers.dev

All 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 convention

Get 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 configured
default. 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 before
generation. 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; the
backend'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 the
prompt (<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 positive
integer no greater than the route's advertised max_output_tokens (including
reasoning). When omitted, the relay applies that allowance. The relay ceiling
is configured with PIONEER_MAX_OUTPUT_TOKENS (default 16384); lower backend
allowances and context capacities take precedence.
Errors: inference errors use {error:{message,type,code}}. Context overflow
returns HTTP 400 before streaming starts, with code: context_length_exceeded and type: invalid_request_error; history is never trimmed or replayed. Missing
capacity returns 503; unavailable concrete models return 400. SSE interruption
or missing terminal finish reasons emit error.code: incomplete_stream and
close 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 upstream
request. Stream failures include retryable: false; do not automatically replay
accepted 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 as
text. 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.

MethodPathBody → 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/streamsame → chunked raw PCM16LE (X-Sample-Rate, X-Channels, X-Sample-Format headers)
POST/api/v1/voicemint a persistent voice_id from a reference clip or designed voice
POST/api/v1/sttmultipart 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/generatemultipart audio, emotion?, include_raw? → JSON facial animation curves for audio you already have
GET/api/v1/face/channelsthe 52 channel names, and which are unsupported
GET/api/v1/face/healthface model, revision and solver version

Directing delivery: control


control changes how a line is spoken without changing the voice. A voice cloned
with 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 the
direction 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 backend
failed. 503 the face backend is not available. Backend errors
carry 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).

MethodPathDescription
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.

MethodPathDescription
GET/companies/createPublic 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/leaderboardPublic directory of named companies, most members first
GET/api/v1/companies/{addr}/infoPublic record + member count
GET/api/v1/companies/{addr}/membersMembers, owner first (members only)
GET/api/v1/companies/{addr}/statusMembers + live connections (members only)
GET/api/v1/companies/{addr}/pendingOwner only: { companyAddress, requests: [{ address, name, memberType, capabilities, requestedAt }] }, oldest first
POST/api/v1/companies/{addr}/approveOwner only: { address } → { approved: true, member }; exactly once (404 afterwards); funds a plan seat while seats remain
POST/api/v1/companies/{addr}/rejectOwner only: { address } → { rejected: true }
POST/api/v1/companies/{addr}/settingsOwner only: { openInvite: boolean }
GET/api/v1/account/companiesEvery company the caller belongs to, with role and joinedAt


Atlas (Assets & Networks)


MethodPathDescription
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)


MethodPathDescription
GET/api/v1/apps/spotlightFeatured 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


MethodPathDescription
GET/healthKubernetes health check → { "status": "ok" }
GET/Server metadata and status
GET/metricsLive 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.