CentroSky API Reference

Base URL: https://centrosky.com/v1

CentroSky is OpenAI-compatible. If you have code using the OpenAI SDK, change

the base URL and the API key and it works — including streaming.

What CentroSky adds on top: multi-agent orchestration. A team of

specialist agents can research, draft, review, and synthesize an answer

together, and you can watch each agent's progress.


Contents

  1. Authentication
  2. Quickstart
  3. Models
  4. Chat Completions
  5. Streaming
  6. Agent Orchestration (sync)
  7. Best-of-N Candidates
  8. Structured code output
  9. Async Tasks
  10. Webhooks
  11. Idempotency
  12. Rate Limits
  13. Errors
  14. SkyPower & Billing

Authentication

Every request needs a bearer token:

http
Authorization: Bearer csky_your_key_here

Create keys at centrosky.com → API. Keys are shown

once at creation. X-CentroSky-Key: csky_... is accepted as an alternative

header.

Never ship a key in client-side code — it carries your SkyPower balance.


Quickstart

cURL

bash
curl https://centrosky.com/v1/chat/completions \
  -H "Authorization: Bearer csky_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "skymind-2",
    "messages": [{"role": "user", "content": "Explain database indexes briefly"}]
  }'

Python (OpenAI SDK)

python
from openai import OpenAI

client = OpenAI(
    api_key="csky_your_key_here",
    base_url="https://centrosky.com/v1",
)

resp = client.chat.completions.create(
    model="skymind-2",
    messages=[{"role": "user", "content": "Explain database indexes briefly"}],
)
print(resp.choices[0].message.content)

Node (OpenAI SDK)

javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "csky_your_key_here",
  baseURL: "https://centrosky.com/v1",
});

const resp = await client.chat.completions.create({
  model: "skymind-2",
  messages: [{ role: "user", content: "Explain database indexes briefly" }],
});
console.log(resp.choices[0].message.content);

Models

GET /v1/models

Model Description
skymind-2 Default. Flagship. Health-aware routing across the strongest live backends. Add agents for orchestration.
flash Fastest and cheapest. Good for classification, short replies, high volume.
sp Balanced mid-tier.
sp+ Legacy premium alias.

skymind-2 resolves to a concrete backend at request time based on provider

health and prompt shape. The backend actually used is always reported in

centrosky.model_actual, so routing is never a black box.

Routing hints

json
{ "model": "skymind-2", "reasoning": true, "messages": [...] }

reasoning: true routes to models stronger at multi-step problems.

task: "code" routes to the dedicated code pool, led by Codestral with the

strongest code-writing general backends behind it. Long prompts automatically

route to a long-context pool. The pool used is reported in

centrosky.routing_pool (general, reasoning, code, long-context).


Chat Completions

POST /v1/chat/completions

Request

Field Type Default Notes
model string skymind-2 See Models
messages array required Standard OpenAI format
max_tokens int 8192 Total output budget, up to 131072. Budgets above a single backend's ceiling are automatically continued across backends until the answer is genuinely complete — see centrosky.segments.
max_segments int 8 Max continuation segments (1–12).
temperature float 0.7
stream bool false See Streaming
agents int \ "auto" Enables orchestration. 1–20.
reasoning bool false Routing hint
task string "code" routes skymind-2 to the dedicated code pool (Codestral-led).
model_prefer array Backend id substrings to try first, e.g. ["deepseek"]. skymind-2 only.
model_exclude array Backend id substrings to never route to. A hard constraint — lets you act on model_actual telemetry. skymind-2 only.
candidates int 2–5. Best-of-N: N generations on N different backends; a reasoning-pool judge picks the winner. Non-streaming skymind-2 only. See Best-of-N.
response_format string "code_files" additionally parses fenced code blocks into structured centrosky.code_files. See Structured code output.

Response

json
{
  "id": "csky-3f9a...",
  "object": "chat.completion",
  "created": 1753243200,
  "model": "skymind-2",
  "choices": [{
    "index": 0,
    "message": { "role": "assistant", "content": "An index is..." },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 14,
    "completion_tokens": 88,
    "total_tokens": 102
  },
  "centrosky": {
    "model_tier": "skymind-2",
    "model_actual": "openai/gpt-oss-120b",
    "provider": "groq",
    "routing_pool": "general",
    "spu_consumed": 0.102,
    "latency_ms": 843,
    "run_id": "3f9a..."
  }
}

The centrosky block is an extension. OpenAI SDKs ignore unknown fields, so

it never breaks compatibility.


Streaming

Set stream: true for token-by-token Server-Sent Events in OpenAI's

chat.completion.chunk format.

python
stream = client.chat.completions.create(
    model="skymind-2",
    messages=[{"role": "user", "content": "Write a haiku about databases"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta
    if delta.content:
        print(delta.content, end="", flush=True)

Raw wire format:

text
data: {"id":"csky-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"csky-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Silent"},"finish_reason":null}]}

data: {"id":"csky-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{...},"centrosky":{...}}

data: [DONE]

Notes:

and SP spend without a second call.

(402, 429, 503). Errors after the first byte arrive in-band as

{"error": {...}} followed by [DONE], because HTTP status can no longer

be changed once headers are sent.

over to another. Once content has been sent it will not splice in a

different model mid-answer — the stream ends with

finish_reason: "length" instead.


Agent Orchestration (sync)

Add agents to any completion request:

bash
curl https://centrosky.com/v1/chat/completions \
  -H "Authorization: Bearer csky_your_key_here" \
  -d '{
    "model": "skymind-2",
    "agents": 5,
    "messages": [{"role":"user","content":"Design a rate limiter for a REST API"}]
  }'

"agents": "auto" lets the router choose the team size.

This is synchronous and can take minutes. For anything beyond a quick

run, use Async Tasks instead — long-held HTTP connections

fail on most proxies, mobile networks, and serverless platforms.

Response adds:

json
"centrosky": {
  "mode": "agents",
  "agent_count": 5,
  "agents_used": ["architect", "coder", "reviewer", "..."],
  "task_id": "9c2e...",
  "spu_consumed": 4.21
}

Best-of-N Candidates

Add candidates (2–5) to a non-streaming skymind-2 completion and the

gateway runs the request on that many different backends in one shot, then

has a reasoning-pool judge pick the best answer:

bash
curl https://centrosky.com/v1/chat/completions \
  -H "Authorization: Bearer csky_your_key_here" \
  -d '{
    "model": "skymind-2",
    "task": "code",
    "candidates": 3,
    "max_tokens": 16000,
    "messages": [{"role":"user","content":"Write a PHP rate limiter class with sliding window"}]
  }'

The winning answer comes back as a normal completion. The tournament is

reported under centrosky.candidates:

json
"centrosky": {
  "mode": "candidates",
  "candidates": {
    "requested": 3,
    "completed": 3,
    "winner": 2,
    "judge_model": "deepseek-reasoner",
    "judge_reason": "Candidate 2 is the only complete implementation with correct window carry-over.",
    "entries": [
      { "candidate": 1, "model": "codestral-latest",    "tokens_out": 1420, "truncated": false, "winner": false },
      { "candidate": 2, "model": "openai/gpt-oss-120b", "tokens_out": 1610, "truncated": false, "winner": true  },
      { "candidate": 3, "model": "deepseek-chat",       "tokens_out": 1388, "truncated": false, "winner": false }
    ]
  }
}

Notes:

used, so you are genuinely comparing different models, not the same one

three times.

spu_consumed cover the whole tournament. This is a quality dial, priced

accordingly.

orders which backends get picked first.

survives, it wins by default. If none survive, the request falls back to a

normal single completion.

steer with model_prefer — the tournament generates its own routing data.


Structured code output

Add response_format: "code_files" to any completion and the gateway parses

fenced code blocks out of the answer server-side:

json
"centrosky": {
  "code_files_count": 2,
  "code_files": [
    { "path": "includes/RateLimiter.php", "language": "php",  "content": "<?php ..." },
    { "path": "app.js",                   "language": "javascript", "content": "..." }
  ]
}

Recognized fence formats: `php:path/file.php (language:path),

`path/file.php (bare path), and `php (bare language — a filename

is synthesized). If the model emits the same path twice, the last version

wins, so self-corrections replace earlier drafts. message.content is

unchanged — the structured files are additive, so existing parsers keep

working.

Combine with task: "code" and candidates for the full code-quality stack.


Async Tasks

The recommended path for orchestration. Submit, get a handle immediately,

poll or receive a webhook.

Create

POST /v1/mind/tasks202 Accepted

bash
curl -X POST https://centrosky.com/v1/mind/tasks \
  -H "Authorization: Bearer csky_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "agents": 5,
    "messages": [{"role":"user","content":"Design a rate limiter for a REST API"}],
    "webhook_url": "https://yourapp.com/hooks/centrosky"
  }'
Field Type Notes
messages or prompt array \ string One is required
agents int \ "auto" 1–20, default "auto"
max_tokens int Default 8192, total budget up to 131072 (continued across backends)
webhook_url string Optional completion callback
json
{
  "id": "9c2e4f1a-...",
  "object": "mind.task",
  "status": "queued",
  "poll_url": "/v1/mind/tasks/9c2e4f1a-...",
  "centrosky": { "mode": "agents", "queue_position": 1 }
}

Poll

GET /v1/mind/tasks/{id}

json
{
  "id": "9c2e4f1a-...",
  "object": "mind.task",
  "status": "executing",
  "progress": {
    "percent": 45,
    "steps_total": 5,
    "steps_completed": 2,
    "agents": [
      { "role": "architect", "model": "gpt-oss-120b", "status": "completed", "latency_ms": 2140 },
      { "role": "coder",     "model": "gpt-oss-120b", "status": "running",   "latency_ms": 0 }
    ]
  },
  "retry_after": 5
}

Statuses: queuedexecutingsynthesizingcompleted (or failed).

Respect Retry-After. It returns 5s normally, tightening to 2s past 80%.

Polling does not count against your rate limit.

On completion:

json
{
  "status": "completed",
  "result": { "role": "assistant", "content": "..." },
  "usage": { "prompt_tokens": 210, "completion_tokens": 1840, "total_tokens": 2050 },
  "centrosky": {
    "spu_consumed": 4.21,
    "latency_ms": 47200,
    "models_used": ["..."],
    "proof_hash": "a3f..."
  }
}

Cancel

DELETE /v1/mind/tasks/{id}

Only works while queued. A running task has already incurred upstream cost

and cannot be cancelled.

List

GET /v1/mind/tasks?limit=20

Polling example

python
import time, requests

H = {"Authorization": "Bearer csky_your_key_here"}

task = requests.post("https://centrosky.com/v1/mind/tasks",
    headers=H, json={"agents": 5, "prompt": "Design a rate limiter"}).json()

while True:
    r = requests.get(f"https://centrosky.com/v1/mind/tasks/{task['id']}", headers=H).json()
    if r["status"] in ("completed", "failed"):
        break
    time.sleep(r.get("retry_after", 5))

print(r["result"]["content"] if r["status"] == "completed" else r["error"])

Webhooks

Pass webhook_url when creating a task and CentroSky POSTs the result when

it finishes.

json
{
  "event": "mind.task.completed",
  "created": 1753243200,
  "data": {
    "id": "9c2e4f1a-...",
    "object": "mind.task",
    "status": "completed",
    "result": { "role": "assistant", "content": "..." },
    "usage": { "total_tokens": 2050 },
    "centrosky": { "spu_consumed": 4.21 }
  }
}

Events: mind.task.completed, mind.task.failed.

Verifying signatures

Every delivery carries:

http
X-CentroSky-Signature: t=1753243200,v1=5f2c8a...

v1 is HMAC-SHA256(secret, "{t}.{raw_body}"). Always verify — otherwise

anyone who learns your endpoint can forge results.

python
import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, sig = parts["t"], parts["v1"]
    if abs(time.time() - int(ts)) > tolerance:
        return False                      # stale — replay attempt
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)
php
function verify(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
    parse_str(str_replace(',', '&', $header), $p);
    if (abs(time() - (int)$p['t']) > $tolerance) return false;
    $expected = hash_hmac('sha256', $p['t'] . '.' . $rawBody, $secret);
    return hash_equals($expected, $p['v1']);
}

Delivery: retried up to 5 times, then marked failed. Respond 2xx to

acknowledge. Redirects are not followed. Treat webhooks as at-least-once and

make your handler idempotent.


Idempotency

Send Idempotency-Key on any POST to make retries safe:

http
Idempotency-Key: 8f14e45f-ea1c-4d2b-9f0e-1a2b3c4d5e6f

Use a fresh UUID per logical operation.

Situation Result
First request Executes normally, response cached
Replay after completion Cached response + Idempotency-Replayed: true
Replay while still running 409
Same key, different body 422
Original failed Not cached — retry executes normally

Keys are scoped to your account and expire after 24 hours. Not supported on

streaming requests, which cannot be meaningfully replayed.

This matters most for agent runs: without a key, a client timeout plus an

automatic SDK retry means the work runs twice and you are billed twice.


Rate Limits

Per-minute request limits by plan. Every response includes:

http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1753243260

X-RateLimit-Reset is a Unix timestamp. On 429 you also get Retry-After

in seconds.

Agent runs cost more than one unit — an agents: 5 request consumes 5

units, because it does roughly five times the upstream work. Polling task

status costs nothing.

Partner keys. First-party integrations can be exempted from per-minute

limits entirely by setting rate_limit_rpm = -1 on the API key row

(centrosky_api_keys). SP metering still applies — this removes the request

throttle, not the billing. Recommended for pipelines that legitimately

multiply request volume (best-of-N candidates, multi-pass review chains).

Handle 429 with exponential backoff:

python
import time, requests

def call(payload, key, attempts=5):
    for i in range(attempts):
        r = requests.post("https://centrosky.com/v1/chat/completions",
                          headers={"Authorization": f"Bearer {key}"}, json=payload)
        if r.status_code != 429:
            return r
        time.sleep(int(r.headers.get("Retry-After", 2 ** i)))
    raise RuntimeError("rate limited after retries")

Errors

Standard OpenAI-compatible envelope:

json
{
  "error": {
    "message": "Rate limit exceeded: 60 requests/min on your plan.",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "retry_after": 12
  }
}
HTTP type Meaning Action
400 invalid_request Malformed body Fix the request
401 authentication_error Missing/invalid key Check the key
402 insufficient_quota Out of SkyPower Top up
404 not_found Unknown task id Check the id
409 invalid_request Idempotency key in flight Wait, then poll
422 invalid_request Idempotency key reused with new body Use a fresh key
429 rate_limit_error Too many requests Back off per Retry-After
500 server_error Internal failure Retry with backoff
502 server_error Upstream provider failed Retry
503 server_error No provider available Retry shortly

402 and 422 are not retryable without changing something. Everything

else is safe to retry with backoff.


SkyPower & Billing

Usage is metered in SkyPower (SP), paid in CENTRO (₵).

Every response reports consumption:

json
"centrosky": { "spu_consumed": 0.102 }

Also exposed as headers on non-streaming completions:

http
X-SkyPower-Consumed: 0.102
X-Request-Id: 3f9a...

SP consumption scales with tokens and a per-model weight — flash costs less

per token than skymind-2. Agent runs consume roughly in proportion to team

size.

Plans, pack pricing, and current balance: centrosky.com/#/pricing

When SP runs out you get 402 with code: "sp_exhausted".


Support

Include centrosky.run_id (or X-Request-Id) when reporting an issue —

it identifies the exact run.


Network SkyPower telemetry

GET /v1/network/skypower

Public aggregate telemetry for explorers and network-status displays. No authentication is required because the response contains no keys, prompts, wallets or user records.

Key sections:

1 SP = 1 SPU/second.

Last updated 4 Aug 2026 · Raw markdown