For developers connecting an application, SDK, agent, or pipeline to the Elysium distributed compute network.
elys_…), not a Supabase session. Keys are scoped and rate limited.Your Elysium administrator creates a key for you in the Enterprise Console (or via POST /api/gateway/keys). You will receive:
| Value | Example | Notes |
|---|---|---|
| Gateway base URL | https://gateway.your-elysium.com | Ask your admin; GET /api/gateway/url returns it |
| API key | elys_a1b2c3d4... | Shown once. Store it in a secret manager |
| Scopes | jobs:create`, `compute:run`, … | Determines which endpoints you can call |
Set them as environment variables for the examples below:
export GATEWAY_URL="https://gateway.your-elysium.com" export ELYSIUM_API_KEY="elys_..."
| Scope | Unlocks |
|---|---|
jobs:create | POST /v1/jobs |
jobs:read | GET /v1/jobs/:id`, `/status`, `/result |
jobs:cancel | DELETE /v1/jobs/:id |
compute:run | OpenAI chat/responses, Anthropic messages, Gemini generate, `POST /v1/wasm/run` |
models:list | GET /v1/models |
embeddings:create | Gemini `embedContent` |
If you call an endpoint without the required scope you get HTTP 403 with code FORBIDDEN (or the provider-specific equivalent).
The gateway accepts the key in any of these forms (use whichever your SDK prefers):
Authorization: Bearer elys_... # standard, works everywhere x-api-key: elys_... # OpenAI / Anthropic style x-goog-api-key: elys_... # Gemini style ?key=elys_... # Gemini query param
Missing/invalid keys → 401. Revoked or expired keys → 401. Always send the key over HTTPS; never embed it in client-side code.
from openai import OpenAI
client = OpenAI(
base_url=f"{GATEWAY_URL}/v1",
api_key=ELYSIUM_API_KEY,
)
resp = client.chat.completions.create(
model="elysium-inference-v1",
messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
print(resp.model_extra["elysium"]["jobId"]) # traceable job idimport OpenAI from "openai";
const client = new OpenAI({
baseURL: `${process.env.GATEWAY_URL}/v1`,
apiKey: process.env.ELYSIUM_API_KEY,
});
const resp = await client.chat.completions.create({
model: "elysium-inference-v1",
messages: [{ role: "user", content: "hello" }],
});import anthropic
client = anthropic.Anthropic(base_url=GATEWAY_URL, api_key=ELYSIUM_API_KEY)
msg = client.messages.create(
model="elysium-inference-v1",
max_tokens=256,
messages=[{"role": "user", "content": "hello"}],
)from google import genai
client = genai.Client(
api_key=ELYSIUM_API_KEY,
http_options={"base_url": GATEWAY_URL},
)
resp = client.models.generate_content(
model="elysium-inference-v1",
contents="hello",
)curl -X POST "$GATEWAY_URL/v1/chat/completions" \
-H "Authorization: Bearer $ELYSIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"elysium-inference-v1","messages":[{"role":"user","content":"hello"}]}'The most direct way to run distributed compute. Submit → poll → fetch result.
Submit — POST /v1/jobs (scope jobs:create)
curl -X POST "$GATEWAY_URL/v1/jobs" \
-H "Authorization: Bearer $ELYSIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "hash batch",
"payload": { "workload": "wasm_hash", "data": "hello" },
"taskCount": 3,
"replicationFactor": 3,
"idempotencyKey": "run-2026-07-18-01"
}'| Field | Type | Notes |
|---|---|---|
payload | object | Workload body passed to the pipeline (required) |
name | string | Display name |
taskCount | int 1–1000 | How many tasks to split into |
replicationFactor | int 1–10 | Replicas per task for verification |
runtimePool | enum | mobile_cpu`, `mobile_gpu`, `pc_gpu`, `ev_edge`, `validator`, `pc_cpu`, `storage |
privatePlaneId | uuid | Route to a specific private plane (must be authorized) |
idempotencyKey | string | Retries with the same key return the original job |
waferSizeBytes | int | Override wafer chunk size |
Response 201 (or 200 on idempotent replay):
{
"success": true,
"data": {
"jobId": "3be228e7-7d0c-4bad-a426-3e2b19087261",
"status": "queued",
"taskCount": 3,
"replicationFactor": 3,
"privatePlaneId": null,
"createdAt": "2026-07-18T19:58:14.753Z",
"idempotentReplay": false,
"poolType": "mobile_cpu"
}
}Poll status — GET /v1/jobs/:jobId/status (scope jobs:read)
{
"success": true,
"data": {
"jobId": "3be228e7-...",
"status": "queued|scheduled|running|completed|failed|cancelled",
"taskCount": 3,
"completedTaskCount": 0,
"verifiedTaskCount": 0,
"createdAt": "...", "updatedAt": "...", "completedAt": null
}
}Fetch result — GET /v1/jobs/:jobId/result (scope jobs:read)
{
"success": true,
"data": {
"jobId": "3be228e7-...",
"status": "completed",
"hasFileOutput": false,
"outputPath": null,
"tasks": [ { "verified": true, "resultHash": "..." } ],
"summary": { "totalTasks": 3, "verifiedTasks": 3, "allVerified": true }
}
}Cancel — DELETE /v1/jobs/:jobId (scope jobs:cancel).
Recommended polling loop: submit, then poll /status every 1–2 s until status is terminal (completed, failed, cancelled), then GET /result.
| Method | Path | Scope |
|---|---|---|
| POST | /v1/chat/completions | compute:run |
| POST | /v1/responses | compute:run |
| GET | /v1/responses/:responseId | compute:run |
| GET | /v1/models | models:list |
chat/completions blocks up to GATEWAY_SYNC_WAIT_SECONDS (default 30). If the job isn't done it returns 504 with code job_timeout and the jobId in the message — fetch it via GET /v1/jobs/:jobId."stream": true to get SSE chat.completion.chunk events terminated by data: [DONE]. Disconnecting the stream cancels the underlying job.POST /v1/responses with "background": true returns 202 with status: queued; poll GET /v1/responses/resp_<jobId>.Idempotency-Key header on POSTs.elysium.jobId for tracing.{ "error": { "message", "type", "param", "code" } }.POST /v1/messages (scope compute:run). max_tokens is required (Anthropic contract). system, temperature, top_p, top_k, stop_sequences supported. "stream": true emits Anthropic SSE events (message_start → content_block_delta → message_stop). Errors: { "type": "error", "error": { "type", "message" } }.
| Method | Path | Scope |
|---|---|---|
| POST | /v1beta/models/{model}:generateContent | compute:run |
| POST | /v1beta/models/{model}:streamGenerateContent | compute:run |
| POST | /v1beta/models/{model}:embedContent | embeddings:create |
Errors use the Google envelope { "error": { "code", "message", "status" } }. Embeddings currently return a deterministic L2-normalized stub vector seeded from the verified job output (default 8 dims, outputDimensionality up to 2048) until real embedding runtimes land on nodes.
POST /v1/wasm/run (scope compute:run) runs a platform-approved WASM module on the secure distributed runtime.
curl -X POST "$GATEWAY_URL/v1/wasm/run" \
-H "Authorization: Bearer $ELYSIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "moduleId": "hash_v1", "input": "bytes to hash", "wait": true }'| Field | Notes |
|---|---|
moduleId | Default `hash_v1`; unknown → `GATEWAY_WASM_MODULE_UNKNOWN` (400) |
input` / `inputBase64 | Workload bytes (mutually exclusive) |
wait | true` → block up to `GATEWAY_SYNC_WAIT_SECONDS`, return verified result on 200 or 202 + `timedOut: true |
taskCount`, `replicationFactor`, `runtimePool`, `privatePlaneId`, `idempotencyKey | Same as `/v1/jobs` |
Without wait, you get 202 immediately — then poll /v1/jobs/:jobId/status.
/v1/containers/run and /v1/onnx/infer are not available — those node runtimes don't exist yet. Calling them will fail.Stateless MCP Streamable HTTP, protocol version 2025-03-26. Authenticate with your API key. The server advertises only tools your scopes allow:
| Tool | Scope | Purpose |
|---|---|---|
elysium_submit_job | jobs:create | Submit a job |
elysium_get_job | jobs:read | Job status |
elysium_get_job_result | jobs:read | Verified results |
elysium_cancel_job | jobs:cancel | Cancel |
curl -X POST "$GATEWAY_URL/mcp" \
-H "Authorization: Bearer $ELYSIUM_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0.0"}}}'Tool execution errors use MCP's isError: true result convention; protocol/auth errors use JSON-RPC error objects.
List the models your key is allowed to use:
curl "$GATEWAY_URL/v1/models" -H "Authorization: Bearer $ELYSIUM_API_KEY"
Model names are validated against the tenant-approved model registry. Unknown models return GATEWAY_MODEL_NOT_APPROVED — they are never silently remapped. Ask your admin to approve a model alias if you need one.
The native and admin surfaces use the Elysium envelope:
{ "success": false, "error": { "code": "FORBIDDEN", "message": "..." } }Provider surfaces use each provider's native error shape (see per-section notes). Common conditions:
| HTTP | When | Code (native / OpenAI) |
|---|---|---|
| 400 | Bad body, unknown WASM module | VALIDATION_ERROR` / `invalid_request_error |
| 401 | Missing/invalid/revoked key | UNAUTHORIZED` / `authentication_error |
| 403 | Missing scope, wrong plane, disabled adapter | FORBIDDEN` / `permission_error |
| 404 | Unknown job/response | NOT_FOUND` / `not_found_error |
| 429 | Rate limit exceeded | GATEWAY_RATE_LIMITED` / `rate_limit_error |
| 502 | Distributed job failed | job_failed |
| 504 | Sync wait elapsed, job still running | job_timeout |
429/504 are expected and retryable. On 504, don't resubmit — the job is still running; fetch it by jobId. Use an idempotencyKey so any retry of a submit can't create a duplicate job.
Each key has a per-minute request limit (fixed window). Exceeding it returns 429. If you need higher throughput, ask your admin to raise rateLimitPerMinute on the key, or use multiple keys per workload.
If your key is locked to a private plane, all your jobs run only on that plane's nodes — you cannot route elsewhere, and request fields can only strengthen (never weaken) that isolation. Some adapters must also have their marketplace card enabled on that plane; if not you'll get GATEWAY_INTEGRATION_NOT_ENABLED. Ask your admin to install the relevant card.
idempotencyKey (native/WASM) or the Idempotency-Key header (OpenAI/Anthropic) on job-creating requests.jobId.idempotencyKey is scoped to your API key and returns the original job on replay.The gateway logs request metadata only (endpoint, status, latency, job id, adapter). Your prompts, payloads, and files are never stored in the gateway request log.
Interactive endpoint list: GET /api/docs on the control plane.
Manage your keys in the API keys dashboard.