Early access · Paid API credit is live. New accounts still get free starter credit.
PricingDocsModelsFAQEarnBlogAboutSign in →

Quickstart

Umbra speaks the OpenAI and Anthropic wire formats. Keep your SDK, change the base URL and the key, and your prompts run on attested Apple-Silicon Macs. Hardware attestation and APNs running-code identity are live for admitted providers; signed per-response receipts remain in progress.

Get an API key

Sign in and mint a key in the console. Keys look like umbra-... and carry a scope plus a rate limit. New accounts start with up to about 10 million free tokens at the lowest input rate, no card required. The example below runs on that free credit out of the box. See pricing for the live per-model rates.

Create an account Mint an API key

Call it (OpenAI-compatible)

# 1. Discover a live model id (the catalog rotates — never hardcode one):
MODEL_ID=$(curl -s https://api.tryumbra.dev/v1/models \
  -H "Authorization: Bearer umbra-..." \
  | jq -r '.data[0].id')

# 2. Call it with that MODEL_ID:
curl https://api.tryumbra.dev/v1/chat/completions \
  -H "Authorization: Bearer umbra-..." \
  -H "Content-Type: application/json" \
  -d "{\"model\":\"$MODEL_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"hello\"}]}"

Or from the OpenAI SDK:

# Python (OpenAI SDK, just repoint it)
from openai import OpenAI
client = OpenAI(base_url="https://api.tryumbra.dev/v1", api_key="umbra-...")

# The catalog is provider-driven; pick a live id instead of hardcoding one.
# Example live model: umbra/qwythos-9b-claude-mythos
model_id = client.models.list().data[0].id

r = client.chat.completions.create(
    model=model_id,
    messages=[{"role": "user", "content": "hello"}],
)
print(r.choices[0].message.content)
// Node
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.tryumbra.dev/v1", apiKey: "umbra-..." });

// The catalog is provider-driven; pick a live id instead of hardcoding one.
// Example live model: umbra/qwythos-9b-claude-mythos
const modelId = (await client.models.list()).data[0].id;

const r = await client.chat.completions.create({
  model: modelId,
  messages: [{ role: "user", content: "hello" }],
});
console.log(r.choices[0].message.content);

Anthropic-compatible

The same key works against /v1/messages with the Anthropic SDK. Set base_url to https://api.tryumbra.dev, pass your key as x-api-key (or use the SDK's apiKey), include anthropic-version: 2023-06-01, and send max_tokens:

# Python (Anthropic SDK)
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.tryumbra.dev",
    api_key="umbra-...",
    default_headers={"anthropic-version": "2023-06-01"}
)

# Example live model: umbra/qwythos-9b-claude-mythos
model_id = client.models.list().data[0].id

msg = client.messages.create(
    model=model_id,
    max_tokens=256,
    messages=[{"role": "user", "content": "hello"}],
)
print(msg.content[0].text)
// Node (Anthropic SDK)
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.tryumbra.dev",
  apiKey: "umbra-...",
  defaultHeaders: { "anthropic-version": "2023-06-01" }
});

// Example live model: umbra/qwythos-9b-claude-mythos
const modelId = (await client.models.list()).data[0].id;

const msg = await client.messages.create({
  model: modelId,
  max_tokens: 256,
  messages: [{ role: "user", content: "hello" }],
});
console.log(msg.content[0].text);

What a response looks like

An example response from umbra/qwythos-9b-claude-mythos on an attested Mac Studio (OpenAI chat-completion shape) — no signup required to preview:

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1720000000,
  "model": "umbra/qwythos-9b-claude-mythos",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "The eclipse crowns the sky in copper light — a ring of fire where darkness wore the sun."
    },
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 12, "completion_tokens": 21, "total_tokens": 33}
}

Errors and retries

The API uses standard HTTP status codes. Common responses:

Every response carries x-request-id and request-id headers — include that value when reporting a failed request. OpenAI-compatible errors expose a stable error.code: rate_limit_exceeded (wait forRetry-After), insufficient_quota (add credit, don't retry), and invalid_api_key. Anthropic clients receive529 overloaded_error when capacity is exhausted. A stream that fails after starting emits a structured error event and omits the trailing [DONE] /message_stop — treat preceding output as partial and retry with backoff.

For 503 (no capacity), the request fails fast — it does not queue. Set trust_level in the request body to control routing:none < self_signed < hardware < code_attested. Higher levels may return 503 if no matching providers are online.

trust_level (optional)

Control the privacy floor per request. Authenticated traffic routed to another provider has a hard code_attested floor; callers cannot lower it. See trust docs for details.

Models

GET /v1/models lists what is live right now. The catalog is auto-register-only and small during alpha, and it rotates with whichever providers are online — so read the id from /v1/models rather than pinning one from this page. It grows as providers bring models online.

Run a provider (Apple-Silicon Mac)

Review the publisher and system changes on the download page, then run the verified installer and guided setup:

# First review https://tryumbra.dev/download/ and its system-change disclosure.
# A first install continues directly into guided setup.
curl -fsSL https://tryumbra.dev/install.sh | sh

Umbra is in alpha: the catalog is intentionally small and grows as providers add models. Developer top-up is live (real card charges via Stripe); connect payout from the provider wallet when available — eligibility is in theprovider agreement. Everything above is live. Questions? See the FAQ or pricing, then start free.