Gateway API · v1

Elysium API Gateway — Developer Guide

For developers connecting an application, SDK, agent, or pipeline to the Elysium distributed compute network.

1. Concepts in 60 seconds
  • Every request you send is translated into one Canonical Elysium Compute Request and run as a distributed job across the network (split into tasks, replicated, and verified by consensus).
  • You authenticate with a gateway API key (elys_…), not a Supabase session. Keys are scoped and rate limited.
  • Jobs are asynchronous under the hood. The provider-compatible endpoints (OpenAI/Anthropic/Gemini) wait synchronously for a short window; the native and WASM endpoints let you submit-and-poll.
  • Responses are verified — content currently reflects the verified consensus output of the distributed run.
2. Get your credentials

Your Elysium administrator creates a key for you in the Enterprise Console (or via POST /api/gateway/keys). You will receive:

ValueExampleNotes
Gateway base URLhttps://gateway.your-elysium.comAsk your admin; GET /api/gateway/url returns it
API keyelys_a1b2c3d4...Shown once. Store it in a secret manager
Scopesjobs: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_..."

Scopes → what you can call

ScopeUnlocks
jobs:createPOST /v1/jobs
jobs:readGET /v1/jobs/:id`, `/status`, `/result
jobs:cancelDELETE /v1/jobs/:id
compute:runOpenAI chat/responses, Anthropic messages, Gemini generate, `POST /v1/wasm/run`
models:listGET /v1/models
embeddings:createGemini `embedContent`

If you call an endpoint without the required scope you get HTTP 403 with code FORBIDDEN (or the provider-specific equivalent).

3. Authentication

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.

4. Quickstart per SDK

OpenAI SDK (Python)

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 id

OpenAI SDK (Node)

import 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" }],
});

Anthropic SDK (Python)

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"}],
)

Gemini SDK (Python)

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",
)

Raw curl

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"}]}'
5. Endpoint reference

5.1 Native jobs API (/v1/jobs)

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"
  }'
FieldTypeNotes
payloadobjectWorkload body passed to the pipeline (required)
namestringDisplay name
taskCountint 1–1000How many tasks to split into
replicationFactorint 1–10Replicas per task for verification
runtimePoolenummobile_cpu`, `mobile_gpu`, `pc_gpu`, `ev_edge`, `validator`, `pc_cpu`, `storage
privatePlaneIduuidRoute to a specific private plane (must be authorized)
idempotencyKeystringRetries with the same key return the original job
waferSizeBytesintOverride 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.

5.2 OpenAI-compatible (/v1)

MethodPathScope
POST/v1/chat/completionscompute:run
POST/v1/responsescompute:run
GET/v1/responses/:responseIdcompute:run
GET/v1/modelsmodels:list
  • Sync wait: non-streaming 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.
  • Streaming: send "stream": true to get SSE chat.completion.chunk events terminated by data: [DONE]. Disconnecting the stream cancels the underlying job.
  • Background: POST /v1/responses with "background": true returns 202 with status: queued; poll GET /v1/responses/resp_<jobId>.
  • Idempotency: send an Idempotency-Key header on POSTs.
  • Every successful chat response carries an elysium.jobId for tracing.
  • Errors use the OpenAI envelope: { "error": { "message", "type", "param", "code" } }.

5.3 Anthropic-compatible (/v1/messages)

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" } }.

5.4 Google Gemini-compatible (/v1beta)

MethodPathScope
POST/v1beta/models/{model}:generateContentcompute:run
POST/v1beta/models/{model}:streamGenerateContentcompute:run
POST/v1beta/models/{model}:embedContentembeddings: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.

5.5 Native WASM compute (/v1/wasm/run)

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 }'
FieldNotes
moduleIdDefault `hash_v1`; unknown → `GATEWAY_WASM_MODULE_UNKNOWN` (400)
input` / `inputBase64Workload bytes (mutually exclusive)
waittrue` → block up to `GATEWAY_SYNC_WAIT_SECONDS`, return verified result on 200 or 202 + `timedOut: true
taskCount`, `replicationFactor`, `runtimePool`, `privatePlaneId`, `idempotencyKeySame 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.

5.6 MCP (agents) — POST /mcp

Stateless MCP Streamable HTTP, protocol version 2025-03-26. Authenticate with your API key. The server advertises only tools your scopes allow:

ToolScopePurpose
elysium_submit_jobjobs:createSubmit a job
elysium_get_jobjobs:readJob status
elysium_get_job_resultjobs:readVerified results
elysium_cancel_jobjobs:cancelCancel
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.

6. Models

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.

7. Error handling

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:

HTTPWhenCode (native / OpenAI)
400Bad body, unknown WASM moduleVALIDATION_ERROR` / `invalid_request_error
401Missing/invalid/revoked keyUNAUTHORIZED` / `authentication_error
403Missing scope, wrong plane, disabled adapterFORBIDDEN` / `permission_error
404Unknown job/responseNOT_FOUND` / `not_found_error
429Rate limit exceededGATEWAY_RATE_LIMITED` / `rate_limit_error
502Distributed job failedjob_failed
504Sync wait elapsed, job still runningjob_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.

8. Rate limits

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.

9. Private planes

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.

10. Idempotency & retries checklist
  • Always set idempotencyKey (native/WASM) or the Idempotency-Key header (OpenAI/Anthropic) on job-creating requests.
  • Treat 202 as "accepted, poll later," not failure.
  • Treat 504 as "still running," not failure — fetch by jobId.
  • Back off on 429 and retry.
  • A given idempotencyKey is scoped to your API key and returns the original job on replay.
11. Privacy note

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.

Support

Interactive endpoint list: GET /api/docs on the control plane.

Manage your keys in the API keys dashboard.