Back to Docs
Atai

SDK & API Reference

v1.0.0

The @atai-group/sdk and the Atai Runtime API

One typed client, one API key, and every capability your application needs — AI, messaging, payments, search, maps and more. Everything a generated (or any) application can do with Atai, documented end to end.

Overview

The Atai Runtime API is the single service boundary between your application and everything Atai operates: AI generation, voice, web search and scraping, email, SMS, WhatsApp, push notifications, maps, scheduling, vector storage, the app database, and checkout. The @atai-group/sdk is its official typed client.

One credential

A project-scoped Atai API key is the only secret your app needs. Provider accounts and provider keys stay inside Atai — always.

One vocabulary

Your app speaks Atai capabilities (ai.text, email, payments…). Atai normalizes every provider response — you never see a provider's wire format.

One meter

Usage is metered and billed in Atai credits per request. No per-provider billing, no per-provider rate limits to juggle.

How the pieces fit together

architecture
Your application (@atai-group/sdk)
        │  Authorization: Bearer atai_<environment>_<secret>
        ▼
Atai Runtime API  (https://atai.ink/api/runtime/v1)
   authenticate → authorize → validate → route
        ▼
Atai providers & infrastructure  (never visible to your app)
  • Typed to the contract. Every capability method mirrors the Runtime API request/response contract 1:1 — invalid input is caught client-side before it ever hits the network.
  • Zero runtime dependencies. Pure fetch + standard JavaScript. Runs in Node.js ≥ 18, serverless, edge runtimes, and browsers.
  • Safe by design. The key is never logged or serialized into errors; error objects carry only code, status, message, and a request ID for support.

Building the product behind the code? The plan-first flow lives in Collaborate & Refine Your Plan →

Installation

Install the SDK from npm:

shell
npm install @atai-group/sdk
pnpm
pnpm add @atai-group/sdk
yarn
yarn add @atai-group/sdk

Requirements

  • Node.js 18 or newer (or any runtime with a global fetch)
  • ES modules (the package ships ESM only)
  • TypeScript ≥ 5 for first-class types — JavaScript works fine too

Runtime environments

The SDK runs anywhere standard JavaScript runs:

Node.js

Express, Next.js server routes, workers — anywhere ≥ Node 18.

Serverless & edge

Vercel functions, Cloudflare Workers, Deno Deploy, Bun.

Browsers

Possible but not recommended for keys — see Security Best Practices.

Generated apps

Atai provisions the key and dependency automatically at build time.

API Keys

Every Runtime request is authenticated with an Atai runtime API key — a project-scoped credential that names exactly one project, one environment, and a set of capability scopes.

Key format

format
atai_<environment>_<secret>

atai_production_a81fK2x9dQ7vN4mZpLw3sE6rT1yU8iO0   ← production key
atai_development_b2cD4eF6gH8jK1lM3nP5qR7s9tU2vW4x  ← development key

The prefix atai_production_ / atai_development_ is part of the key itself and is safe to display — dashboards show it as the key's identity. The remaining secret portion is what authenticates requests and is never stored in plaintext by Atai.

Environments

development

For your dev/test build. Isolated usage records and limits. Create freely while building.

production

For your live application serving real users. Treat its keys with production-grade care.

Creating keys

Keys are managed from your project workspace — Workspace → Runtime → API keys:

  1. Open your project and go to the Runtime section
  2. Click Create key, choose the environment
  3. Optionally restrict scopes (leave empty to grant all capabilities the project is entitled to)
  4. Optionally set an expiry date
  5. Copy the secret immediately — it is shown exactly once and can never be retrieved again

Generated applications don't need this step: Atai provisions a scoped key automatically and injects it into the app's secret store (see Auto-Provisioning).

Create, rotate and monitor keys, watch usage & health, and test every endpoint against your real key in the Developer Portal →

Scopes

A scope equals a capability ID. An empty scope list grants every capability the project's plan and entitlement allow; a non-empty list grants exactly those capabilities. Requests outside a key's scopes are rejected with 403 runtime_capability_not_allowed.

example scopes
["ai.text", "email", "payments"]   ← only these three capabilities
[]                                 ← all entitled capabilities

Rotation & revocation

  • Rotate replaces a key atomically: a new secret is issued (shown once), the old secret stops working immediately. Lineage is preserved on both keys.
  • Revoke permanently disables a key. History is preserved; revoked keys can never authenticate again.
  • Expiry — keys may carry a future expiry timestamp; expired keys are rejected like revoked ones (401 runtime_key_expired).

Treat your key like a password

Never commit it to Git, never ship it to the browser, never paste it into client-side code. If a key leaks, rotate it from the dashboard — the leaked secret stops working instantly.

Quickstart

Initialize the client once per credential and call a capability:

initialize
import { Atai } from "@atai-group/sdk"

const atai = new Atai({
  apiKey: process.env.ATAI_API_KEY!,   // atai_<environment>_<secret>
  // baseUrl is optional — defaults to Atai's production runtime origin.
})
your first AI call
const result = await atai.ai.chat({
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Write a haiku about databases." },
  ],
  temperature: 0.7,
  max_tokens: 200,
})

console.log(result.content)             // the assistant's reply
console.log(result.model)               // the model Atai actually used
console.log(result.usage?.totalTokens)  // token usage for this call
verify connectivity
const health = await atai.health.check()

console.log(health.status)               // "operational"
console.log(health.identity.projectId)   // your project, from the key record
console.log(health.identity.environment) // "development" | "production"

One client per credential

The client is an instance model: create one per key/environment. Configuration is validated at construction — requests can never change the credential or the origin afterward. Concurrent calls are safe and fully isolated.

multiple environments
const dev = new Atai({ apiKey: process.env.ATAI_DEV_KEY! })
const prod = new Atai({ apiKey: process.env.ATAI_PROD_KEY! })

Cancellation & timeouts

Pass a standard AbortSignal. A default 60-second per-request timeout applies. Cancellations and timeouts are always reported as distinct atai_aborted / atai_timeout errors — never masked as network or provider failures.

aborting a request
const controller = new AbortController()
setTimeout(() => controller.abort(), 5000)

try {
  await atai.ai.chat({ messages: [{ role: "user", content: "Long task?" }] }, { signal: controller.signal })
} catch (e) {
  // e.code === "atai_aborted" when cancelled by the caller
}

Capabilities

Each capability is a namespace on the client. Under the hood every call is the same Runtime request — { capability, operation, input } — so the SDK's types always mirror the wire contract.

AI

atai.ai

Text generation, completion, and embeddings. The workhorse for assistants, content, and understanding.

chatcompletionembed

Image generation

atai.image

Text-to-image (and image-to-image with reference photos) — product shots, illustrations, and mockups.

generateImage

Video generation

atai.video

Text-to-video as an async job — submit a prompt, poll for the finished clip.

generateVideo

Speech & transcription

atai.speech · atai.transcribe

Text-to-speech with built-in voices, and audio-to-text across languages and formats.

speaktranscribe

Vision

atai.vision

Multimodal understanding — send images, PDFs, audio or video alongside text for analysis.

visionInput

Voice

atai.voice

Turn text into natural speech audio (returned base64-encoded with its MIME type).

synthesize

Search

atai.search

Query the open web and get ranked, described results.

web

Web

atai.web

Scrape a public URL into clean markdown, with links and truncation info.

scrape

Email

atai.email

Transactional email — receipts, resets, digests, notifications.

send

SMS & WhatsApp

atai.sms · atai.whatsapp

Text messages and WhatsApp messaging to phone numbers.

send

Notifications

atai.notifications

Push notifications to a user's device via its platform token.

send

Maps

atai.maps

Addresses to coordinates and back, with optional country and language hints.

geocodereverseGeocode

Calendar

atai.calendar

List upcoming bookings and create new ones with attendees.

listBookingscreateBooking

Vectors

atai.vectors

Store embeddings and search them — semantic search, recommendations, RAG.

upsertsearchdelete

Database

atai.db

The app's runtime database: filter, sort, paginate, and mutate records.

querycreateeditdelete

Payments

atai.payments

Create checkout sessions for one-time purchases and order flows.

createCheckout

ai — text generation

ts
const result = await atai.ai.chat({
  model: "openai/gpt-5.2",       // optional — Atai's default model otherwise
  messages: [
    { role: "system", content: "You are concise." },
    { role: "user", content: "Summarize this order." },
  ],
  max_tokens: 300,               // 1–1,000,000
  temperature: 0.4,              // 0–2
  stop: ["###"],                 // string or up to 4 strings
  seed: 42,
})

// result: { id, model, content, finishReason, usage? }
ParameterTypeDescription
messagesrequiredAiMessage[]1–128 messages; each content is 1–100,000 characters. Roles: user, assistant, system.
modeloptionalstringCaller-selected model identifier (max 200 chars). Omitted = Atai's configured default.
temperatureoptionalnumber0–2. Sampling randomness.
top_poptionalnumber(0, 1]. Nucleus sampling.
max_tokensoptionalnumber1–1,000,000. Maximum tokens to generate.
stopoptionalstring | string[]Up to 4 stop sequences, each max 100 chars.
frequency_penaltyoptionalnumber-2 to 2.
presence_penaltyoptionalnumber-2 to 2.
seedoptionalnumberInteger seed for (best-effort) reproducibility.

email — send email

ts
await atai.email.send({
  to: ["customer@example.com"],
  subject: "Your receipt",
  text: "Thanks for your purchase!",
  html: "<p>Thanks for your purchase!</p>",
  replyTo: "support@yourapp.com",
})
// → { id }

sms / whatsapp — send messages

ts
await atai.sms.send({ to: "+15551234567", body: "Your code is 8291" })
await atai.whatsapp.send({ to: "+15551234567", body: "Order confirmed 🎉" })
// → { messageId, status, segments? }

db — the app database

ts
const { records, total } = await atai.db.query({
  tableName: "orders",
  filter: { status: "paid" },
  sortBy: "createdAt",
  sortDirection: "desc",
  limit: 20,
  skip: 0,
})

const created = await atai.db.create({ tableName: "orders", data: { item: "Latte", qty: 2 } })
const updated = await atai.db.edit({ tableName: "orders", recordId: created.id!, data: { status: "paid" } })
await atai.db.delete({ tableName: "orders", recordId: created.id! })

payments — checkout

ts
const checkout = await atai.payments.createCheckout({
  items: [{ name: "Pro plan", quantity: 1, price: 19.99 }],
  customerEmail: "customer@example.com",
  successUrl: "https://yourapp.com/success",
  cancelUrl: "https://yourapp.com/pricing",
  idempotencyKey: "order-8291",   // deduplicates retries
  currency: "USD",
})
// → { checkoutId, checkoutUrl, status } — redirect the customer to checkoutUrl

Multimodal — images, video, speech, transcription, vision

ts
// Image generation (text-to-image, or image-to-image via input_references)
const image = await atai.image.generateImage({
  prompt: "A red panda astronaut floating in space",
  model: "google/gemini-2.5-flash-image",   // OpenRouter model id
  aspect_ratio: "16:9",
})
// → { images: [{ b64, mediaType }] } — base64-encoded image bytes

// Video generation (async job — submit, then poll with { jobId })
const job = await atai.video.generateVideo({
  prompt: "A timelapse of a city skyline at dusk",
  model: "google/veo-3",
  duration: 5,
})
// → { jobId, status: "submitted" } — poll: generateVideo({ jobId: job.jobId })

// Text-to-speech (returns base64 audio)
const speech = await atai.speech.speak({
  input: "Hello from Atai!",
  model: "openai/gpt-4o-mini-tts-2025-12-15",
  voice: "alloy",
  response_format: "mp3",
})
// → { audioBase64, mimeType }

// Transcription (speech-to-text; base64 audio in)
const text = await atai.transcribe.transcribe({
  input_audio: { data: audioBase64, format: "wav" },
  model: "openai/whisper-large-v3",
  language: "en",   // optional hint
})
// → { text }

// Vision — multimodal understanding (images, PDFs, audio, video + text)
const answer = await atai.vision.visionInput({
  messages: [{
    role: "user",
    content: [
      { type: "text", text: "What is in this image?" },
      { type: "image_url", image_url: { url: "https://example.com/photo.jpg" } },
      // Also supported: { type: "file", file: { file_data } } for PDFs,
      // { type: "input_audio", input_audio: { data, format } },
      // { type: "video_url", video_url: { url } }
    ],
  }],
})
// → { content, model? }

Everything else

ts
// Voice — text to speech
const audio = await atai.voice.synthesize({ text: "Hello!", voiceId: "optional" })
// → { audioBase64, mimeType, voiceId, modelId, characters }

// Web search
const found = await atai.search.web({ query: "best coffee in Kampala", limit: 5 })
// → { results: [{ title, url, description? }], creditsUsed? }

// Scrape a public URL
const page = await atai.web.scrape({ url: "https://example.com" })
// → { url, title?, markdown?, links?, truncated?, creditsUsed? }

// Push notification
await atai.notifications.send({ token: deviceToken, title: "New order", body: "Table 4 ordered." })

// Maps
const places = await atai.maps.geocode({ query: "Kampala", limit: 5 })
const rev = await atai.maps.reverseGeocode({ longitude: 32.58, latitude: 0.34 })

// Calendar
const bookings = await atai.calendar.listBookings({ startDate: "2026-10-01", limit: 10 })
await atai.calendar.createBooking({ title: "Consultation", startTime: "2026-10-02T09:00:00Z", endTime: "2026-10-02T10:00:00Z" })

// Vectors — semantic search
await atai.vectors.upsert({ vectors: [{ id: "doc-1", values: embedding, metadata: { page: 1 } }] })
const hits = await atai.vectors.search({ vector: embedding, topK: 5 })
await atai.vectors.delete({ ids: ["doc-1"] })

REST API Reference

The SDK wraps the plain HTTP Runtime API. You can use the REST surface directly (from any language) — the contract below is identical to what the SDK sends.

Base URL & auth

base
https://atai.ink/api/runtime/v1
auth header
Authorization: Bearer atai_production_xxxxxxxxxxxxxxxx
POST/api/runtime/v1· Bearer key

Invoke any capability. This is the single invocation endpoint.

curl
curl -X POST https://atai.ink/api/runtime/v1 \
  -H "Authorization: Bearer $ATAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capability": "ai.text",
    "operation": "chat",
    "input": {
      "messages": [{ "role": "user", "content": "Hello!" }]
    }
  }'
GET/api/runtime/v1· public

Capability discovery — lists every registered capability and its operations. Registry metadata only; it does not promise provider availability.

GET/api/runtime/v1/health· Bearer key

Authenticated health check — proves the key is active and the runtime context resolves. Shares the invocation path's authentication. Returns safe metadata only.

200 OK
{
  "ok": true,
  "data": {
    "status": "operational",
    "requestId": "rtreq_9f2c41e8a7b64d0e9c3f8a2b7d5e1c60",
    "identity": {
      "projectId": "proj_yourproject",
      "environment": "production",
      "apiKeyId": "rkey_01h9x2",
      "scopes": ["ai.text"]
    },
    "key": { "status": "active", "lastUsedAt": 1758451200000 },
    "lifecycle": ["authenticated", "scope_validated", "capability_resolved",
                  "model_resolved", "provider_selected", "provider_request",
                  "response_normalized", "usage_recorded", "charge_recorded"]
  }
}

Key management — session-authenticated dashboard APIs

These endpoints belong to the Atai dashboard, not to the SDK. They authenticate with your Atai session (cookie), not a runtime key, and ownership is always derived server-side.

GET/api/runtime/keys?projectId={id}· session
POST/api/runtime/keys?projectId={id}· session
GET/api/runtime/keys/{keyId}· session
DELETE/api/runtime/keys/{keyId}· session
POST/api/runtime/keys/{keyId}/rotate· session
POST /api/runtime/keys body
{
  "name": "My worker",                  // optional, max 100 chars
  "environment": "production",          // "development" | "production"
  "scopes": ["ai.text", "email"],       // [] = all entitled capabilities
  "expiresAt": 1798761600000            // optional absolute epoch-ms
}

The create and rotate responses carry the plaintext secret exactly once. It is never persisted in plaintext and never returned again.

Request body

ParameterTypeDescription
capabilityrequiredstringHigh-level capability, e.g. "ai.text". See Capabilities.
operationrequiredstringProvider-neutral operation within the capability, e.g. "chat".
inputoptionalobjectOperation input — validated per capability. Unknown fields are rejected, never forwarded.
metadataoptionalobjectOptional caller metadata. Never treated as identity or authorization.

Identity fields (userId, projectId, environment, apiKeyId) and pricing fields (credits, price, providerCost) are structurally forbidden in the body — sending them is a validation error. Identity comes exclusively from the authenticated key.

Response envelope

Every response — success or failure — uses one envelope:

success
{
  "ok": true,
  "data": {
    "requestId": "rtreq_…",       // correlation ID — also in x-request-id header
    "capability": "ai.text",
    "operation": "chat",
    "data": {                      // provider-neutral payload
      "id": "gen_…",
      "model": "openai/gpt-5.2",
      "content": "…",
      "finishReason": "stop",
      "usage": { "promptTokens": 14, "completionTokens": 42, "totalTokens": 56 }
    }
  }
}
failure
{
  "ok": false,
  "error": {
    "code": "runtime_rate_limited",
    "message": "Too many requests. Please retry later."
  }
}

The correlation ID accompanies every request lifecycle (success and failure) in the x-request-id response header. Include it when contacting support — it connects your request to its exact server-side trace.

Errors & Retries

Every SDK failure is an AtaiError with a normalized code, the HTTP status when one exists, and the server's requestId for support correlation. Runtime-originated failures keep their canonical runtime_* codes; the SDK adds its own atai_* codes for transport and configuration problems. The SDK never reveals which provider Atai used.

error handling
import { AtaiError, isAuthenticationError, isRateLimitError } from "@atai-group/sdk"

try {
  await atai.ai.chat({ messages: [{ role: "user", content: "Hello" }] })
} catch (e) {
  if (e instanceof AtaiError) {
    console.error(e.code)       // runtime_* or atai_*
    console.error(e.status)     // e.g. 401, 403, 429, 502
    console.error(e.requestId)  // correlation ID for support

    if (isAuthenticationError(e)) {
      // 401 — the key is missing, invalid, revoked or expired: rotate it.
    } else if (isAuthorizationError(e)) {
      // 403 — the key works but lacks the scope for this capability.
    } else if (isRateLimitError(e)) {
      const waitMs = e.retry?.afterMs  // safe retry metadata when provided
      // back off and retry
    }
  }
}

Runtime error codes

CodeHTTPMeaning & what to do
runtime_authentication_error401Invalid or missing API key. Check the key is set and well-formed.
runtime_key_revoked401This key was revoked. Rotate to a new key.
runtime_key_expired401This key passed its expiry. Rotate to a new key.
runtime_authorization_error403The key is not allowed to perform this action.
runtime_project_access_denied403The key does not have access to the requested project.
runtime_capability_not_allowed403The key's scopes don't include this capability. Create a key with broader scopes.
runtime_rate_limited429Atai's per-key limit was hit. Back off using retry metadata, then retry.
runtime_provider_rate_limited429The upstream provider is rate limiting. Distinct from Atai's own limit — retry with backoff.
runtime_insufficient_entitlement402The account backing this key is out of credits. Top up to continue.
runtime_invalid_request422The body is invalid for this capability — check the contract tables above.
runtime_provider_error502The upstream provider failed. Safe to retry once; if it persists, contact support with the requestId.
runtime_provider_timeout504The upstream provider timed out. Safe to retry.
runtime_model_unavailable400The requested model isn't available. Drop the model field or pick another.
runtime_capability_unavailable501The capability exists but has no working provider yet.
runtime_billing_unavailable503Billing could not complete the charge (fail-closed). Retry shortly.
runtime_pricing_unconfigured503This capability isn't enabled for billing yet. Contact the platform administrator.

SDK error codes

CodeMeaning
atai_missing_api_keyNo API key was passed to new Atai().
atai_invalid_api_keyThe key doesn't match the atai_<environment>_<secret> shape.
atai_invalid_configMalformed configuration (e.g. bad baseUrl).
atai_invalid_requestClient-side request validation failed — fix the input before retrying.
atai_network_errorfetch failed / DNS / connection failure. Check connectivity.
atai_timeoutThe 60-second per-request timeout elapsed.
atai_abortedThe caller aborted via AbortSignal.
atai_invalid_responseMalformed JSON or runtime envelope — retry, then report with the requestId.

Safe to log. Error messages, String(error) and JSON.stringify(error) contain only code/status/requestId/message — never your API key or provider internals.

Limits & Quotas

Requests per key per minute

300

Platform ceiling — project limits may tighten it, never raise it.

Request body size

256 KB

Larger bodies are rejected with 413 before parsing.

In-flight per process

32

Runtime capacity cap — excess requests get a fail-fast 429, never a queue.

Per-request timeout

60 s

Client-side default in the SDK; cancellable via AbortSignal.

Messages per chat call

128

Each message content: 1–100,000 characters.

Key-management rate

20/day

Key creations and rotations per account, abuse protection.

How rate limiting works

  • Limits are enforced per API key, per rolling minute. Hit the limit and the runtime answers 429 runtime_rate_limited — invalid keys can never consume provider capacity.
  • Provider-side rate limits are normalized separately: runtime_provider_rate_limited means the upstream provider (not Atai) is throttling — retry with exponential backoff.
  • Concurrency is fail-fast by design: the runtime holds a bounded number of provider slots and returns 429 immediately when full, instead of queueing requests in memory.

Best practices

  • Retry 429/502/504 with exponential backoff and jitter; honor retry.afterMs when provided.
  • Set max_tokens deliberately — it bounds both latency and credit cost.
  • Use idempotency keys on checkout creation to make retries safe.

Credits & Billing

Runtime usage is billed in Atai credits, deducted from the account that owns the project — automatically, per request. There is no per-provider billing and no provider account to maintain.

How charging works

1

Request authenticated

The key resolves to its project and account. Identity and billing context come from the key record — never from the request.

2

Provider executes

The runtime routes your capability to its provider and normalizes the response.

3

Usage recorded & charged

Provider-reported usage (tokens, characters, credits — whatever the capability's unit is) is normalized and billed in Atai credits. Only the request lifecycle's charge is recorded; the account sees one clean ledger entry.

  • Fail-closed billing. If the billing subsystem cannot complete a charge, the request is denied (503 runtime_billing_unavailable) rather than served for free.
  • Unpriced = denied. Operations without an admin-configured price are rejected before any provider spend (503 runtime_pricing_unconfigured).
  • Out of credits. Requests fail with 402 runtime_insufficient_entitlement until the account tops up.
  • The SDK doesn't bill. Credits, metering and ledger writes are Atai's server responsibility; the SDK only surfaces usage metadata included in a response.

Platform-level operations (building, analyzing, collaborating) have their own credit costs — see Credits & Billing in the main docs →

Security Best Practices

Keep keys server-side

Atai runtime keys are secret credentials — there is no browser-safe public key type. If a generated application runs entirely in a public browser, the key can be seen by end users. Call the SDK from your server (or a serverless function) and proxy results to the browser. Never compensate by embedding any provider key in the client — the SDK has no fields for them and never will.

the right shape: thin server proxy
// app/api/chat/route.ts (server) — the key never leaves the server
import { Atai } from "@atai-group/sdk"

const atai = new Atai({ apiKey: process.env.ATAI_API_KEY! })

export async function POST(req: Request) {
  const { message } = await req.json()
  const result = await atai.ai.chat({
    messages: [{ role: "user", content: message }],
    max_tokens: 500,
  })
  return Response.json({ reply: result.content })
}

Environment hygiene

  • Store keys in environment variables or your platform's secret manager — never in source, never in localStorage or cookies (the SDK itself never writes your key anywhere).
  • Use a development key while building and a production key in live deployments. Different keys = isolated usage and limits.
  • Scope keys narrowly — if a worker only sends email, give it ["email"], not every capability.
  • Rotate on a schedule and rotate immediately on suspicion of leakage. Rotation is atomic: the old secret dies the moment the new one is issued.

What the runtime guarantees

  • Identity is server-derived. userId/projectId/environment come exclusively from the key record — client-supplied identity fields are structurally rejected.
  • Keys are hashed at rest. Atai stores only a SHA-256 hash; plaintext is shown once at creation and never retrievable.
  • Provider neutrality. Provider names, wire formats and credentials never cross the Runtime boundary — there is nothing provider-specific to leak or configure.
  • One-way traffic. The SDK sends nothing anywhere except your requests to the Atai origin. No telemetry, no per-request base-URL overrides, no storage.

Auto-Provisioning

If your application was generated by Atai, the runtime credential is delivered to you — no dashboard step needed. When the build completes, Atai:

  1. Creates a project-scoped runtime key with least-privilege scopes
  2. Injects it into your application's secret store as ATAI_API_KEY
  3. Discards the plaintext — it is never persisted in plaintext, logged, or returned again
in a generated app — zero configuration
import { Atai } from "@atai-group/sdk"

// The key arrives via the secret store as ATAI_API_KEY —
// provisioned automatically at build completion.
const atai = new Atai({ apiKey: process.env.ATAI_API_KEY! })

// Optionally verify the wiring at startup:
const health = await atai.health.check()
if (health.status !== "operational") throw new Error("Atai Runtime not connected")

Environment mapping

Build completion → development

The dev preview gets a development key.

Production deploy → production

The live deployment gets its own production key.

Idempotent by design

Provisioning is safe to retry: exactly one active key per (project, environment) exists at any time. If secret injection fails, the freshly created key is revoked immediately — no orphaned credentials — and the retry provisions a fresh one.

Provider credentials never enter your app. The generated application receives only its own project-scoped runtime credential. Provider keys stay server-side at Atai — permanently.

FAQ

Is @atai-group/sdk public?

Yes — it is published to the public npm registry so any application (generated or not) can install it with a plain npm install. The package is proprietary to Atai (all rights reserved): you may use it to talk to the Atai Runtime API, but its source is not open-source.

Which provider is behind ai.chat?

That's the point of the abstraction — you never need to know. Atai selects and operates providers server-side, normalizes every response into Atai's shapes, and never exposes provider names or wire formats through the SDK.

Can I call the Runtime API without the SDK?

Yes. The REST surface is documented above — POST /api/runtime/v1 with a Bearer key and a { capability, operation, input } body works from any language. The SDK exists for typed input/output, client-side validation, normalized errors, and cancellation support.

How do I get an API key for my own (non-generated) project?

Create a project in Atai, open Workspace → Runtime → API keys, and create a key. Generated apps get a key automatically at build time.

What happens when my credits run out?

Runtime requests fail with 402 runtime_insufficient_entitlement until the account is topped up. Keys stay valid — nothing is revoked; the next top-up resumes usage immediately.

Does the SDK work in the browser?

Technically yes (pure fetch, zero dependencies) — but runtime keys are secret credentials with no public-key variant, so a browser-embedded key is visible to end users. Route calls through your server instead. See Security Best Practices.

How do I report a problem with a specific request?

Capture the requestId from the thrown AtaiError (or the x-request-id response header) and contact support — it correlates to the exact server-side trace, including which stage of the lifecycle handled your request.

Is there a sandbox or test mode?

Use a development-environment key: identical behavior and contract, isolated usage records and limits — safe experimentation without touching production data or budgets.

Ready to build?

Install the SDK, verify your key with one health check, and make your first AI call in under a minute.

Related documentation