Back to chat
Your agentsTerms & Privacy
Developer API

The Shadow-OS agent, as an API

Premium multi-agent reasoning at a fraction of the usual API cost. One REST endpoint, your API key, JSON in & out — with fully isolated conversations so every end-user of your app keeps their own private memory and files. A free monthly tier is included — no credit card required.

Fraction of the cost
Premium quality from ultra-efficient models.
One simple endpoint
POST your prompt, get a JSON answer.
Keys you control
Up to 4 keys, revoke anytime.

Your API keys

Loading…

Quickstart

Send a POST request to the endpoint below with your API key in the Authorization header.

POSThttps://shadow-os-backend.onrender.com/api/v1/agent
curl -X POST https://shadow-os-backend.onrender.com/api/v1/agent \ -H "Authorization: Bearer sk-shadow-..." \ -H "Content-Type: application/json" \ -d '{"input": "Summarize the latest AI news", "session_id": "demo"}'

Authentication

Every request must include your secret key — as a Bearer token, or via the X-Shadow-API-Key header. Keep it server-side; never expose it in client-side code.

Authorization: Bearer sk-shadow-... # or X-Shadow-API-Key: sk-shadow-...

Request body

inputstringrequiredThe prompt / instruction for the agent.
session_idstringoptionalThe conversation id. Each distinct value is its OWN isolated conversation — separate memory & files. Reuse it to continue that conversation with full context. Defaults to "default".
scopestringoptional"session" (default) isolates memory & files per session_id. "account" shares one memory/file space across all your calls (and enables your account's connected integrations).

Response

{ "answer": "I packaged the project into cloud_cost_anomaly_review.zip — download it below.", "code": null, "request_id": "a1b2c3d4", "session_id": "demo", "files": [ { "name": "cloud_cost_anomaly_review.zip", "download_url": "https://…/api/artifacts/…/cloud_cost_anomaly_review.zip?exp=…&sig=…", "mime": "application/zip" } ], "download_url": "https://…/api/artifacts/…/cloud_cost_anomaly_review.zip?exp=…&sig=…", "usage": { "used": 1, "quota": 75, "remaining": 74, "period": "2026-06" } }

Files the agent produces

When the agent builds something — a ZIP, a report, code, an image — the response carries it in files. There is no attachment and no multipart body: the signed download_url is the file. Fetch it to get the bytes.

  • files is always present — an empty array when the turn produced nothing.
  • download_url at the top level is a shortcut to the first file, or null.
  • Links are signed and time-limited (exp + sig) — download promptly, or call again for a fresh link.
  • Files belong to the session_id that created them (or to your account under scope: "account").
# build something and download whatever came back curl -s -X POST https://shadow-os-backend.onrender.com/api/v1/agent \ -H "Authorization: Bearer $SHADOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input":"Build a small FastAPI project and package it as a zip","session_id":"demo"}' \ | jq -r '.files[].download_url' \ | xargs -I {} curl -sOJL "{}"

Conversations & isolation

Building an app that serves many end-users? Every session_id is a fully isolated conversation — its own memory, its own uploaded files, its own context. Nothing bleeds between your users. This is the default; you don't have to do anything to get it.

New conversation
Send a fresh session_id → a clean, empty, isolated space. No memory or files from any other conversation.
Continue a conversation
Reuse the same session_id → the agent picks up with all of that conversation's memory and files.
Never mixed
Memory, files and RAG are all keyed to the conversation — user A can never see user B's data.
// user A's chat — isolated, remembers only user A { "input": "remember I prefer short answers", "session_id": "user-A" } // user B's chat — a totally separate brain & file space { "input": "what are my preferences?", "session_id": "user-B" } // → "You haven't told me any preferences yet." (A's are invisible here) // continue user A later — full memory + files come back { "input": "what are my preferences?", "session_id": "user-A" } // → "You prefer short answers."

Uploads follow the same rule — pass the matching session_id (form field) to /api/v1/upload, /api/v1/search and /api/v1/documents so a file is visible only inside its conversation.

Want one shared assistant instead (a single memory across all your calls, plus your account's connected Gmail/Calendar/integrations)? Add "scope": "account" to the body. An isolated conversation is a clean sandbox and intentionally has no access to your account integrations. Billing and quota always stay on your key, regardless of scope.

Endpoints

POST/api/v1/agentRun the agent on a prompt (chat, reasoning, tools). · 1 quota unit
POST/api/v1/uploadUpload a document (pdf/txt/md/csv/docx) into your knowledge base. · 1 quota unit
POST/api/v1/searchSemantic search over your uploaded documents. · 1 quota unit
GET/api/v1/documentsList the documents in your knowledge base. · free
GET/api/v1/usageYour current quota + lifetime usage statistics. · free
Upload a file (multipart):
curl -X POST https://shadow-os-backend.onrender.com/api/v1/upload \ -H "Authorization: Bearer sk-shadow-..." \ -F "file=@report.pdf" \ -F "session_id=demo" # same session_id you pass to /api/v1/agent

The session_id matters: an upload is isolated to that conversation, so the agent can only use it when you send the same id. Omit it and the file lands in the default conversation. Use -F "scope=account" to share it across every call on your key instead.

Errors

Errors use standard HTTP status codes and return a JSON body with a machine-readable error and a human-readable detail.

{ "error": "quota_exceeded", "detail": "Free monthly quota reached. Resets next month.", "request_id": "a1b2c3d4" }
401invalid_keyMissing or invalid API key.
402quota_exceededFree monthly quota reached.
429rate_limitedToo many requests — retry after a short wait.
400bad_requestMalformed body, or the 'input' field is missing.

Agent Platform API

Which one do I use? The Agent API above is the fastest path — one endpoint, isolated per session_id. The Agent Platform here is for a configured product: a persona + shared org knowledge + your connected integrations, driven by one token that doubles as a human magic-link, a WhatsApp chat, and a REST key. Each end-user still gets their own private memory.

Configure an agent (persona, knowledge, rules) once, then run it for any number of end-users. Memory and chat history are isolated per member (via member_key) — the agent learns each person separately and never mixes two people — while knowledge and integrations are shared across the org. Strict per-agent, per-member isolation, fail-closed.

Run a configured agent
POST {API}/api/agent/run
Content-Type: application/json

{
  "token":      "sk-agent-…",   // the agent token (manager or member)
  "input":      "Do you deliver to Florentin?",
  "member_key": "user-42",       // ← per-PERSON: private memory + history
  "session_id": "main"           // ← conversation id (multi-conversation orgs)
}
member_key — a stable id for each end-user. The agent learns and remembers each one separately (never mixes two people). session_id — keep it constant for one continuous thread, or vary it to run multiple parallel conversations per user. Public agent info: GET /api/agent/info?token=….

Full, durable history (members are not guests): every member’s conversations are saved server-side and listable —GET /api/agent/threads?token=&member_key= lists a member’s conversations;GET /api/agent/history?token=&member_key=&thread_id= returns one conversation’s messages. Both are scoped to that member (fail-closed) — no one reads another member’s threads.
Create & manage agents — with your API key
POST {API}/api/v1/agents
Authorization: Bearer sk-shadow-…

{ "template": "support", "fields": { "business_name": "Acme" },
  "config": { "webhook": "https://you.com/hook", "collect_contact": true } }
→ { "agent": {…}, "manager_token": "sk-agent-…" }   // run it via /api/agent/run
Pure-API lifecycle (Bearer sk-shadow-…):
· POST /api/v1/agents — create · GET /api/v1/agents — list · PATCH /api/v1/agents/:id — persona, rules & full config
· define your own end-users with member_key (each gets private memory + a living profile) · separate chats with session_id
· GET /api/agent/threads · GET /api/agent/history — a user's conversations
· config.webhook — we POST escalation AND outcome events to your server in real time
· config.integrations — connect your own HTTP tools the agent calls mid-chat · config.collect_contact, config.fallback, config.tone, config.require_login, config.memory_mode
With your signed-in session (Bearer JWT) — the full console, by API:
· POST /api/agents/:id/knowledge (text) · POST /api/agents/:id/knowledge/file (PDF/txt/md/csv/docx) · GET/DELETE …/knowledge
· POST /api/agents/:id/tokens (mint, limits) · POST /api/agents/:id/share (opaque links) · GET /api/agents/:id/members (with profiles)
· GET /api/agents/:id/appointments · GET /api/agents/:id/analytics (leads · bookings · quotes · resolved · handoffs)
· GET /api/agents/:id/escalations + stale-customer alerts · POST …/:eid/answer (the human-in-the-loop)

Reference

Access
Registered users only — create keys while signed in
Auth
Header Authorization: Bearer sk-shadow-… (or X-Shadow-API-Key)
Body
input (required) · session_id (optional — its own isolated memory + files) · scope (optional — "session" default | "account" shared)
Free quota
75 requests / user / month, then HTTP 402
Rate limit
Max 10 requests / 20s per user → HTTP 429
Keys
Up to 4 active keys per account
Errors
401 invalid key · 402 quota exceeded · 429 rate-limited