Skip to content

Configure Your App

Reference for configuring your workspace to support mobile and web app integration. Covers external auth providers, the CMS storefront, credit setup, and push notification preferences.

Base URL: https://athena-dev-api.leanscale.com

Swagger UI: https://athena-dev-api.leanscale.com/swagger-ui/


1. Architecture Overview

Athena is an AI agent orchestration platform. Each workspace is a self-contained application backend that hosts agents, manages consumer customers, handles billing, and serves CMS content.

┌──────────────┐ ┌────────────────────────────────────┐
│ │ eak_ (verified) │ Athena Workspace │
│ Mobile App │────────────────────▶│ │
│ │ ◀── SSE stream ── │ ┌─ Agent Execution (sync/stream) │
└──────────────┘ │ ├─ Conversations + Memory │
│ ├─ Credit System (billing) │
┌──────────────┐ wak_ (server) │ ├─ CMS Storefront (pages/agents) │
│ Your Server │────────────────────▶│ ├─ External Auth (Firebase/Clerk) │
│ │ │ └─ RevenueCat (subscriptions) │
└──────────────┘ │ │
│ → OpenAI, Anthropic, Google, ... │
┌──────────────┐ webhook └────────────────────────────────────┘
│ RevenueCat │────────────────────▶ POST /webhooks/{ws}/revenuecat
└──────────────┘

Two Integration Patterns

PatternAuthUse Case
Mobile/Web appFirebase JWT → token exchange → eak_ keyEnd-user facing apps with per-user billing
Server-to-serverwak_ key directly, or wak_ → mint eak_ keysBackend services, bots, internal tools

Key Concepts

  • wak_ — Workspace API Key. Long-lived, server-side only. Never expose to clients.
  • eak_ — Ephemeral API Key. Short-lived (1–24h), safe for client-side. Scoped to specific agents.
  • External Customer — A user from your auth system (Firebase UID, Clerk ID). Has credit balances, conversation history, execution records.
  • Credits — Three-bucket ledger: active (spendable now), frozen (top-ups locked while subscription lapsed), inactive (voided/refunded, audit only). Subscription credits forfeit at period end; top-ups freeze on lapse and unfreeze on any future subscription purchase.

2. EAK-Callable Endpoint Matrix

Every endpoint in this guide is reachable with an ephemeral API key (eak_) or a workspace API key (wak_) via the X-API-Key header.

MethodPathRequired scopeNotes
POST/auth/external/{workspace_id}none (token exchange)Issues eak_ from Firebase/Clerk JWT
POST/ephemeral-keys/createephemeral_key:create (wak_)Server-side mint
POST/agents/{id}/executeagent:executeSync, JSON response
POST/agents/{id}/execute/streamagent:executeSSE
POST/agents/{id}/execute/batchagent:executeBulk
POST/agents/{id}/preview-creditsagent:executeCost calc, no execution
POST/executions/{id}/tool-resultagent:executeClient-tool resume
POST/executions/{id}/resumeagent:executeHumanGate / crashed-execution resume
POST/attachments/uploadagent:executeMultipart
GET/my/conversationsconversation:readList own
POST/my/conversationsconversation:createGet-or-create
POST/my/conversations/{id}/archiveconversation:createSoft-close
GET/my/conversations/{id}/messagesconversation:readPage of messages
POST/my/conversations/{cid}/messages/{mid}/feedbackconversation:read or conversation:createThumbs ± memory feedback
GET/my/conversations/{id}/variablesconversation:readSticky vars snapshot
PATCH/my/conversations/{id}/variablesconversation:createPatch sticky vars
GET/my/mediamedia:readGenerated assets
GET/my/attachmentsagent:executeList own uploads
DELETE/my/attachments/{id}agent:executeDelete own upload
GET/my/preferencesagent:executePush prefs read
POST/my/preferencesagent:executePush prefs write
GET/my/creditsagent:executeBalance
GET/my/notifications/deliveriesagent:executePush delivery log
GET/public/cms/...cms:readStorefront reads

Scope hierarchy: conversation:create implies conversation:read. The default scopes minted by token exchange are agent:execute + conversation:create.


3. Authentication

Token Exchange (Mobile/Web Apps)

Your app authenticates users through Firebase (or Clerk), then exchanges that token for an Athena eak_ key.

POST /auth/external/{workspace_id}
Content-Type: application/json
{
"provider": "firebase",
"token": "eyJhbGciOiJSUzI1NiIs..."
}

Response (200):

{
"data": {
"key": "eak_a1b2c3d4e5f6...",
"expires_at": "2026-03-19T12:00:00Z",
"customer": {
"id": "019f902b-db70-7ea4-8ef2-60a8ff5747cd",
"external_id": "firebase_uid_xyz",
"email": "user@example.com",
"display_name": "Jane Doe",
"active_credits": 100.0,
"frozen_credits": 0.0,
"inactive_credits": 0.0
}
}
}

The returned eak_ key is valid for 24 hours. Use it in all subsequent requests as X-API-Key: eak_....

Rate limits:

  • Per IP: 10/min, 50/hr (checked before JWT verification)
  • Per identity: 5/min, 20/hr (checked after JWT verification)

Error responses:

  • 403 — External auth not enabled for workspace
  • 400 — Invalid provider or token format
  • 401 — JWT verification failed
  • 429 — Rate limit exceeded

Server-Side Ephemeral Key Minting

POST /ephemeral-keys/create
X-API-Key: wak_your_workspace_key
{
"name": "user-session-12345",
"purpose": "agent_execution",
"scopes": ["agent:execute"],
"ttl_seconds": 3600,
"agent_ids": [
"019f902b-db6f-7ea4-8ef2-60a8ff5747cd",
"019f902b-db71-7ea4-8ef2-60a8ff5747cd"
]
}
  • agent_ids restricts which agents this key can execute. Empty array = all agents.
  • ttl_seconds range: 60–86400 (1 min to 24 hours).
  • The raw key is returned only once — store it for the session.

Key Renewal

eak_ keys expire. Your app should:

  1. Track expires_at from the creation response.
  2. Renew before expiry by calling token exchange or ephemeral key creation again.
  3. Handle 401 responses by triggering re-auth.

4. Agent Execution

All execution endpoints accept X-API-Key: eak_... (or wak_...).

Request Body

{
"message": "Compose a follow-up email for the client meeting",
"attachments": [
{
"filename": "notes.pdf",
"content_type": "application/pdf",
"storage_key": "attachments/user_abc/a1b2c3.pdf"
}
],
"variables": {
"tone": "professional",
"recipient": "John Smith"
},
"conversation_scope": "019f902b-db72-7ea4-8ef2-60a8ff5747cd",
"external_user_id": "firebase_uid_xyz",
"client_tools": [
{
"name": "query_transactions",
"description": "Query the user's local transaction ledger",
"parameters": {
"type": "object",
"properties": {
"start_date": { "type": "string", "format": "date" },
"end_date": { "type": "string", "format": "date" }
},
"required": ["start_date", "end_date"]
}
}
]
}
FieldRequiredDescription
messageConditionalUser input text (1–65,536 chars). Required when agent’s requires_user_message: true.
attachmentsNoFile attachments via storage_key reference (max 10).
variablesNoInput variables for wizard/workflow agents. Capped at 16 KiB.
conversation_scopeNoConversation ID — enables message history injection and auto-persistence.
external_user_idNoYour user’s ID — enables per-user memory personalization.
client_toolsNoClient-fulfilled tool definitions. Max 32 per request.

When Is message Required?

Each published AgentVersion carries requires_user_message.

Flag valueCaller obligationServer behaviour if message omitted
trueMust send non-empty message400 Bad Request with code: "MESSAGE_REQUIRED"
falseMay omit messageExecutes; no synthetic user-message row inserted

Sync Execution

POST /agents/{agent_id}/execute
X-API-Key: eak_...

Blocks until the workflow completes.

Response (200):

{
"data": {
"execution_id": "019f902b-db71-7ea4-8ef2-60a8ff5747cd",
"status": "completed",
"output": {
"text": "Dear John,\n\nThank you for the productive meeting...",
"usage": {
"input_tokens": 150,
"output_tokens": 280,
"total_tokens": 430
},
"cost_microdollars": 4200
},
"latency_ms": 2341
}
}

Status values: completed, awaiting_approval, waiting_for_external_event, awaiting_client_tool, failed.

Streaming Execution (SSE)

POST /agents/{agent_id}/execute/stream
X-API-Key: eak_...
Content-Type: application/json

Returns a text/event-stream.

Key events:

EventWhenUse For
startAlways firstGet execution_id for tracking
textEach LLM token chunkAppend to displayed text
reasoningThinking/reasoning chunksShow thinking indicator
tool_callLLM invokes a server toolShow tool execution UI
tool_resultServer tool returns resultShow tool output
tool_call_awaiting_clientLLM invokes a client toolExecute tool locally and POST result
node_start / node_completeWorkflow node lifecycleProgress indicators
media_gen_submittedImage/video gen startedShow generating indicator
media_gen_status_updateGeneration progressUpdate queue position
media_gen_completeImage/video gen doneRender the generated media
heartbeatEvery 15s during gen waitKeep connection alive (ignore in UI)
finalWorkflow completeStream is done. Extract final usage/cost.
errorExecution failedShow error, credits are auto-refunded

If the client disconnects before final, the execution is marked Failed and pre-deducted credits are refunded.

Batch Execution

POST /agents/{agent_id}/execute/batch
X-API-Key: eak_...
{
"items": [
{ "key": "item-1", "message": "Summarize this article: ..." },
{ "key": "item-2", "message": "Translate to French: ..." }
]
}

Returns SSE with batch_startedbatch_progress (per item) → batch_completed.

Idempotency

Prevent duplicate executions (and double credit charges) by sending an Idempotency-Key header.

POST /agents/{agent_id}/execute
X-API-Key: eak_...
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
ScenarioResult
First request with key abcProcessed normally
Second request with same key abc409 Conflict — no execution, no credit charge
Request without Idempotency-KeyProcessed normally (idempotency is opt-in)
Same key after 24 hoursProcessed normally (key expired)

Credit Preview

POST /agents/{agent_id}/preview-credits
X-API-Key: eak_...
{ "inputs": { "tone": "professional", "length": "long" } }

Returns the credit calculation with base cost and applied rule breakdown.

Client-Fulfilled Tools (Suspend/Resume)

Client-fulfilled tools let the LLM invoke functions that run on the consumer device rather than on the server. Essential for apps where data lives locally.

Prerequisites:

  • The AI node must have accepts_client_tools: true.
  • max_client_tools caps how many tools a single request can declare (default 32).

Flow:

  1. Consumer app sends client_tools in the execute request.
  2. If the LLM chooses a client tool, execution suspends — status becomes awaiting_client_tool.
  3. SSE emits tool_call_awaiting_client + suspended_awaiting_client_tools.
  4. Consumer app runs each tool locally and POSTs the result:
POST /executions/{execution_id}/tool-result
X-API-Key: eak_...
{
"tool_call_id": "tc_abc123",
"outcome": "success",
"result": { "transactions": [...], "total": 5.50 }
}
  1. Last fulfilled call resumes execution. Response carries the terminal state.

Add Accept: text/event-stream to the final-fulfillment POST to stream the resumed execution’s events instead of blocking.

Error results:

{
"tool_call_id": "tc_abc123",
"outcome": "error",
"code": "database_locked",
"message": "Local database is currently syncing"
}

Tool name rules:

  • Must match ^[a-z][a-z0-9_]{0,63}$
  • Must not collide with server-side tools on the agent
  • parameters must be a JSON Schema object ("type": "object")

Structured Output (Native JSON Mode)

When an agent’s AI node has output_schema configured, the execution response includes parsed_output alongside text:

{
"data": {
"output": {
"text": "{\"type\":\"answerCard\",\"hero\":{\"amount\":\"$2,450\"}}",
"parsed_output": {
"type": "answerCard",
"hero": { "amount": "$2,450" }
}
}
}
}

Use parsed_output when available. Fall back to text and parse manually if parsed_output is null.


5. File Attachments & Vision

Athena supports sending files (images, PDFs, documents) alongside messages. Vision-capable models (GPT-4o, Claude 3.5 Sonnet, Gemini 1.5) automatically interpret image content.

Upload Flow (Two-Step)

Step 1: Upload the file

POST /attachments/upload
X-API-Key: eak_...
Content-Type: multipart/form-data
file: <binary file data>

Response (200):

{
"data": {
"storage_key": "attachments/user_abc123/a1b2c3d4.jpg",
"filename": "photo.jpg",
"content_type": "image/jpeg",
"size_bytes": 245120
}
}

Step 2: Reference in execute request

Pass the returned storage_key in the attachments array.

Limits

ConstraintValue
Max attachments10 per request
Max file size50 MiB per file
storage_keyMust start with "attachments/"

Supported Content Types

CategoryTypesHandling
Imagesimage/jpeg, image/png, image/gif, image/webpSent as vision content
PDFsapplication/pdfSent as document content
Texttext/plain, text/csv, text/markdownInlined as text in prompt

Listing & Deleting Uploads

GET /my/attachments — list the calling customer’s uploads.

Query params: conversation_id, content_type, cursor, limit (default 20, max 100).

DELETE /my/attachments/{attachment_id} — soft-delete one row. Returns 204 No Content.


6. Conversations & Messages

For multi-turn chat agents, use conversations to maintain message history.

Create or Resume a Conversation

POST /my/conversations
X-API-Key: eak_...
{ "agent_id": "019f902b-db6f-7ea4-8ef2-60a8ff5747cd" }

Returns 201 Created (new) or 200 OK (existing active conversation).

Start a Fresh Chat

Archive the current conversation first — the next get-or-create will create a fresh row:

POST /my/conversations/{conversation_id}/archive
X-API-Key: eak_...
Idempotency-Key: <uuid>

Execute with Conversation History

Pass conversation_scope in your execution request:

{
"message": "Make it more formal",
"conversation_scope": "019f902b-db72-7ea4-8ef2-60a8ff5747cd"
}

This injects prior messages as context and auto-persists the user message + agent response.

Message Roles

roleAuthorReplay to LLM?
"user"The end userYes
"assistant"The agent (LLM output)Yes
"tool"Tool execution resultYes
"system"The Athena platform (failure events, handoffs)No

System Messages — Failure & Lifecycle Events

When an execution fails, the backend appends a system message to the conversation:

{
"role": "system",
"content": "Generation failed: storage timeout while uploading thumbnail",
"execution_id": "019f902b-db71-7ea4-8ef2-60a8ff5747cd",
"metadata": {
"failed": true,
"error_type": "stream_error",
"source": "stream"
}
}

Client rendering requirements:

  1. Render distinct from assistant messages (different bubble style — error state).
  2. Show content verbatim — it is already user-safe.
  3. Surface a retry affordance using execution_id.
  4. Filter from any local LLM replay.

Conversation Variables

Read: GET /my/conversations/{id}/variables

Write: PATCH /my/conversations/{id}/variables

{
"variables": { "tone": "friendly", "recipient": "Jane" },
"labels": { "tone": "Tone", "recipient": "Recipient Name" }
}

Merge semantics — existing keys not in the patch body are preserved.

Message Feedback

POST /my/conversations/{cid}/messages/{mid}/feedback
X-API-Key: eak_...
{ "value": 1 }

value: 1 (thumbs up) or -1 (thumbs down). Triggers memory reinforcement or suppression for the associated memory node.


7. Public CMS API (Storefront)

These endpoints serve your storefront content — agent catalog, pages, categories. All require cms:read scope.

Common headers:

  • X-API-Key: wak_... (or eak_ with cms:read scope)
  • X-Preferred-Language: ar (optional)
  • ?lang=ar (query param, highest priority)
  • Response: Cache-Control: public, max-age=1800
  • Response: ETag: W/"abc..." (conditional requests supported)

Language Resolution

Priority order: ?lang=arX-Preferred-Language header → workspace default.

CMS Endpoints

EndpointDescription
GET /public/cms/settingsWorkspace CMS config (languages, cache TTL, consent)
GET /public/cms/pagesList all active pages (metadata only)
GET /public/cms/pages/{slug}Full page with components + resolved_agents map
GET /public/cms/agentsPaginated agent catalog (search, category, tag filters)
GET /public/cms/agents/{id}Single agent detail with input_schema for wizard agents
GET /public/cms/categoriesCategory tree with agent counts
GET /public/cms/tagsFlat list of tags

Component Types

TypeDescription
hero_bannerFull-width hero section
agent_cardSingle featured agent
gridGrid of agent cards
category_tabsTabbed category navigation
carouselImage/content carousel
static_textRich text/markdown block
sectionVisual grouping wrapper
sliderHorizontal scroll
example_galleryGallery of agent examples

8. Credit System

Credits are workspace-scoped. Each external customer has two buckets:

BucketSourceBehavior
Subscription CreditsActive subscription grantReset on renewal. Consumed first.
Consumable CreditsAd-hoc purchasesNever expires. Consumed second.

Checking Balance

Every response to an eak_ request with an external customer includes:

X-Credits-Remaining: 75.0

For an explicit balance fetch: GET /my/credits[?generation_cost=N]

{
"data": {
"active_credits": 50.0,
"frozen_credits": 25.0,
"inactive_credits": 0.0,
"next_refresh_at": "2026-05-05T10:00:00Z",
"recommended_topup_pack_id": null,
"recommended_topup_pack_credits": null,
"recommended_topup_pack_currency": null,
"frozen_card_dismissed_at": null
}
}

active_credits is the spendable balance. frozen_credits > 0 means the customer has top-ups locked behind a lapsed subscription — render the “Resubscribe to unlock” CTA. Add ?generation_cost=N to receive a recommended top-up pack big enough to cover that cost.

Insufficient Credits

When credits run out, execution endpoints return:

HTTP 402 Payment Required
{
"error": {
"code": "payment_required",
"message": "Purchase credits to continue."
}
}

Catch 402 and show a “buy credits” prompt.

Trials and Free Credits

Athena does not mint workspace-configured free credits. Trial or intro access comes from App Store / Play products managed through RevenueCat; when RevenueCat sends a trial or intro subscription purchase, Athena applies the normal subscription entitlement and product credit grant.


9. Response Headers & App Versioning

Every authenticated response includes:

HeaderValue
X-Credits-RemainingCurrent credit balance (eak_ + customer only)
X-App-VersionThe workspace’s current published AppVersion ID
X-Workspace-IdEcho of the workspace

Use X-App-Version to detect when your app’s CMS content needs revalidation. Cache the value locally; when it changes, refetch CMS data.


10. Error Reference

All errors follow:

{
"error": {
"code": "snake_case_error_code",
"message": "Human-readable description.",
"details": {}
}
}
HTTP StatusMeaning
400Validation error or bad request body
401Missing or invalid API key / JWT
402Insufficient credits
403Valid credentials but missing permission / scope
404Resource not found (or hidden — no existence leak)
409Conflict (duplicate idempotency key, execution not in expected state)
422Semantic validation failed (e.g. agent config invalid)
429Rate limit exceeded — see Retry-After header
500Internal server error — credits auto-refunded for execution failures

11. Customer Preferences & Push Registration

POST /my/preferences
X-API-Key: eak_...
{
"push_token": "ExponentPushToken[xxx]",
"push_provider": "expo",
"language": "en",
"timezone": "America/New_York",
"notifications_enabled": true
}

Supported push_provider values: expo, fcm, apns.


12. Push Notifications

Athena can deliver push notifications to customers when:

  • An async execution completes
  • A HumanGate is approved or rejected
  • A batch job finishes
  • Workspace admins trigger a broadcast

Rate limits: 10 notifications per customer per hour, with 60-second deduplication.

Delivery log: GET /my/notifications/deliveries


13. Apple Live Activities

For iOS apps using Live Activities (Dynamic Island / Lock Screen widgets):

POST /my/preferences
{
"live_activity_push_token": "...",
"live_activity_id": "019f902b-db71-7ea4-8ef2-60a8ff5747cd"
}

The server sends ActivityKit push updates during long-running executions.


14. Message Validation Rules & Error Codes

RuleLimitError Code
message max length65,536 charsvalidation_error
variables payload16 KiBvalidation_error
attachments count10 per requestvalidation_error
client_tools count32 per request (default)validation_error
Tool name format^[a-z][a-z0-9_]{0,63}$invalid_tool_name
Attachment storage_keyMust start with "attachments/"invalid_storage_key

15. Per-Conversation Turn Lock

Only one execution can be in-flight per conversation at a time.

If your app submits while a turn is processing, the server returns:

HTTP 409 Conflict
{
"error": {
"code": "conversation_turn_in_progress",
"message": "A turn is already in progress for this conversation."
}
}

Wait for the current final event (or poll GET /executions/{id}/status) before submitting the next message.


16. Multi-Suspend Execution Contract

An execution can suspend multiple times in a single run — e.g. the LLM calls multiple client tools sequentially. After each resume:

  • If the LLM calls another client tool → execution suspends again (new awaiting_client_tool status, new pending_tool_call_ids).
  • If the LLM finishes → execution transitions to completed / failed.

The loop is bounded by the agent’s max-turns config, not the resume mechanism.


17. Background → Foreground Recovery

When the app returns to foreground:

  1. Check X-App-Version — if changed, refetch CMS data.
  2. For any conversation that was executing, poll GET /executions/{id}/status.
  3. If status is completed or failed, fetch the last message from the conversation (GET /my/conversations/{id}/messages?limit=1) to sync the UI.
  4. If status is still in-flight, reconnect the SSE stream (streaming is resumable — pass the execution_id as the stream URL parameter).