Developer docs
Quick start
Plenum Cloud exposes an OpenAI-compatible API. Existing OpenAI SDK projects migrate by changing only base_url and the API key.
1. Register and create a key under "API Keys" in the console (plaintext shown once). 2. Point base_url at Plenum. 3. Call as usual.
API
| Endpoint | Description |
|---|---|
POST /v1/chat/completions | Chat completions with streaming. Request/response shapes match OpenAI; streaming uses SSE and the final chunk carries usage. |
GET /v1/models | Models currently routable. |
GET /healthz | Public health check returning ok; the status page probes it. |
Authentication
All /v1 requests use Authorization: Bearer <API key>. Keys start with pln-; revocation takes effect immediately.
Streaming
Set stream: true. The gateway automatically injects stream_options.include_usage so the final SSE chunk carries usage — the basis for metering; no manual setup needed.
Metering & usage
Each request is metered and priced across four token classes, reported in the usage object:
| Field | Meaning |
|---|---|
usage.prompt_tokens | Total input tokens (including cached) |
usage.prompt_tokens_details.cached_tokens | Input tokens served from prefix cache, billed at a much lower rate |
usage.completion_tokens | Output tokens |
usage.completion_tokens_details.reasoning_tokens | Chain-of-thought tokens for reasoning models (when returned) |
The console "Usage" and "Billing" views use the same definitions and drill down by model and key; invoices follow platform metering.
Prefix caching
Cached input tokens cost far less than uncached ones, and the saving is passed through in pricing. Agent workloads (long system prompts, tool definitions, multi-turn history) hit naturally — provided the prefix is byte-stable:
| Practice | Why |
|---|---|
| Put the system prompt and tool definitions first and keep them fixed | Caching matches by prefix; any change early in the sequence invalidates everything after it |
| Place dynamic content (timestamps, user names, retrieved docs) at the end | Preserves the shared prefix |
| Append messages in multi-turn rather than rewriting history | Earlier turns' KV state is reused directly |
Hit rate is observable per key in the console.
Rate limits & quotas
Each API key has its own per-minute request limit (token bucket with short bursts) and an optional monthly token quota. Exceeding either returns 429 with error.code identifying the cause; clients should retry with exponential backoff. Dedicated-endpoint limits follow the contract.
Error codes
Errors follow the OpenAI shape: { "error": { "code", "message" } }.
| HTTP | code | Meaning |
|---|---|---|
| 400 | bad_request | Body is not valid JSON or lacks required fields |
| 401 | invalid_api_key | Key invalid or revoked |
| 429 | rate_limit_exceeded | Per-minute request limit exceeded; retry later |
| 429 | insufficient_quota | Monthly token quota exhausted |
| 502 | upstream_error | All candidate upstreams unavailable (after automatic retry and failover) |
| 502 | dedicated_unavailable | The node bound to your dedicated endpoint is unavailable; dedicated traffic never falls back to the shared pool |
4xx errors are not retried; connection failures, 5xx and upstream 429s fail over across candidate upstreams automatically.
Dedicated endpoints
A dedicated endpoint is exclusive inference capacity: your requests route only to the node bound to you, never shared with other customers or subject to shared-pool limits; billed by capacity × time regardless of token usage.
| Step | Description |
|---|---|
| 1 | Submit a request under "Dedicated" in the console: model, GPU type, count |
| 2 | Operations reviews, binds a node and confirms the hourly rate; status moves from pending to running and billing starts |
| 3 | From then on, calling that model with your API key pins to the dedicated node automatically |
| 4 | Release the instance in the console to stop billing; invoices show dedicated capacity as monthly line items |
Compute onboarding
Teams with idle GPUs can list clusters for rent. Requirement: the node exposes an OpenAI-compatible streaming chat-completions endpoint (vLLM / SGLang etc.), reachable over the internet or a private link.
| Step | Description |
|---|---|
| 1 | Register the node under "Compute" in the console: name, endpoint, credentials, models served, hourly rate |
| 2 | The platform probes the node periodically with real inference requests (not /healthz), recording TTFT, throughput and failure rate |
| 3 | After manual approval the node joins scheduling; traffic is allocated by measured performance, and consecutive failures remove it temporarily |
| 4 | Every request served by your node is metered line by line; request and token counts are visible in the console and settled monthly |
Console API
The management API behind the console lives under /admin/v1, authenticated with a session token (Authorization: Bearer <session>), separate from API keys. Useful for automating usage and billing queries:
| Endpoint | Description |
|---|---|
POST /admin/v1/auth/login | Email + password → session token (valid 7 days; changing the password revokes sessions on other devices) |
GET /admin/v1/me | Current account status: pending / active / rejected. New accounts must be approved by an administrator (active) before using other endpoints or the API |
GET /admin/v1/keys · POST · DELETE /{id} | List / create / revoke keys |
GET /admin/v1/usage · /usage/series | Usage totals and time series (by model, by key) |
GET /admin/v1/billing | Invoice: usage lines + dedicated capacity monthly slices |
GET /admin/v1/logs | Request log (own keys only) |
GET /admin/v1/nodes · /instances | Compute nodes and dedicated instances |
GET · POST /admin/v1/topups | Top-up orders: WeChat QR (credited instantly on payment) or bank transfer (credited once finance confirms) |
GET /admin/v1/earnings · POST /admin/v1/payouts | Supplier earnings (by node × month) and payout requests |
POST /admin/v1/chat?key=<id> | Used by the console Playground: metered to one of your API keys; body identical to /v1/chat/completions |