Shadow-OS Python SDK
The official Python client for Shadow-OS — build, operate and talk to AI agents.
pip install shadow-os
from shadow_os import ShadowOS
with ShadowOS() as client: # reads $SHADOW_OS_API_KEY
print(client.chat("Summarise the notes I uploaded yesterday"))
- Typed — every response is a dataclass, the package ships
py.typed, and unknown server fields stay reachable via.raw. - Sync and async —
ShadowOSandAsyncShadowOSshare one definition per endpoint, so they cannot drift. - Resilient — timeouts, exponential backoff with jitter,
Retry-Aftersupport, and automatic retries for cold starts and 5xx. - Complete — everything the platform can do, not just chat: agents, knowledge, access links, customers, escalations, appointments, analytics.
Authentication
Two credentials, for two different jobs.
| Credential | Looks like | Use it for |
|---|---|---|
| Account key | sk-shadow-… |
Your account: chat, documents, and managing agents you own. |
| Agent token / share code | sk-agent-… / lnk_… |
Talking to one configured agent, as your end-users do. |
client = ShadowOS("sk-shadow-…") # or set SHADOW_OS_API_KEY
agent = AgentClient("lnk_…") # or set SHADOW_OS_AGENT_TOKEN
Create an account key at /developers.
Your account assistant
reply = client.chat("What did we agree with Dana?", session_id="conv-42")
print(reply.answer, reply.usage)
session_id is an isolation boundary, not just a label: memory, uploaded files and search are scoped to
it, so one end-user can never see another's. Pass scope="account" to opt into the shared account space
(which also has access to connected integrations such as Gmail).
Documents & semantic search
client.documents.upload("handbook.pdf", session_id="conv-42")
for doc in client.documents.list(session_id="conv-42"):
print(doc.name)
for hit in client.documents.search("refund policy", top_k=5, session_id="conv-42"):
print(hit.score, hit.source, hit.text[:120])
Quota
u = client.usage()
print(f"{u.used}/{u.quota} used — {u.remaining} left")
Building an agent
from shadow_os import ShadowOS, AgentConfig
client = ShadowOS()
created = client.agents.create(
name="Nona Pizza",
template="pizzeria", # or persona="..." for full control
fields={"business_name": "Nona", "hours": "Sun–Thu 11:00–23:00"},
config=AgentConfig(memory_mode="single", tone="warm and brief"),
)
print(created.id, created.manager_token) # the token is shown ONCE — store it now
Browse what's available before you build:
for t in client.templates():
print(t.id, t.name, [f["id"] for f in t.fields])
print(client.tool_catalog().groups) # optional tool bundles you can enable per agent
Teaching it
agent = client.agent(created.id)
agent.knowledge.add_file("menu.pdf", description="Full menu with prices")
agent.knowledge.add_file("storefront.jpg") # images are understood AND sendable to customers
agent.knowledge.add_url("https://nona.example/about")
agent.knowledge.add_text("We deliver within 4km. Closed on holidays.", name="policies")
agent.knowledge.add_sendable("invoice-template.pdf") # deliverable, but NOT searchable knowledge
Sharing it
link = agent.share_link() # persistent, created once, stable forever
print(link.web, link.whatsapp)
staff = agent.access.create_share_link(role="manager", label="Front desk")
api = agent.access.create_token(role="member", label="Website widget")
Operating it
for c in agent.customers():
print(c.display_name, "·", c.profile)
for e in agent.escalations():
agent.answer_escalation(e.id, "Yes — we're open until 23:00 on Sunday.")
print(agent.analytics().raw)
print(agent.appointments())
print(agent.delivery_status())
Talk to it as its manager — it sees your customer roster and acts with your authority:
print(agent.talk("Message Dana that her order is ready"))
Talking to an agent (what you embed)
from shadow_os import AgentClient
with AgentClient("lnk_…") as agent:
info = agent.info()
print(info["agent_name"], info["welcome"])
reply = agent.send("Are you open on Sunday?", member_key="user-7f3c")
print(reply.answer, reply.files)
member_key is the identity of the person you are speaking for. Give each end-user a stable, unguessable
id and each gets private, durable memory and history inside the agent — they can never see each other.
dana = agent.for_member("user-7f3c", member_name="Dana") # shares the connection pool
for conv in dana.conversations():
print(conv.session_id, conv.title)
for msg in dana.history(session_id="main"):
print(msg.role, msg.content)
Async
Identical surface, awaited:
from shadow_os import AsyncShadowOS
async with AsyncShadowOS() as client:
reply = await client.chat("hello")
agents = await client.agents.list()
stats = await client.agent(agents[0].id).analytics()
Errors
from shadow_os import QuotaExceeded, RateLimited, ShadowOSError
try:
client.chat("hello")
except QuotaExceeded as e:
print("out of quota:", e.usage)
except RateLimited as e:
print("slow down, retry in", e.retry_after)
except ShadowOSError as e:
print(e.status, e.code, e.message, e.request_id)
Every error carries status, code, message and — when the server sent one — request_id. Quote that id
in a support request and the exact call can be found in the logs.
| Exception | HTTP |
|---|---|
BadRequestError |
400 / 422 |
AuthenticationError |
401 |
QuotaExceeded |
402 |
PermissionDeniedError |
403 |
NotFoundError |
404 |
ConflictError |
409 |
PayloadTooLarge |
413 |
RateLimited |
429 |
ServerError / ServiceUnavailable |
5xx |
APITimeoutError / APIConnectionError |
no response |
Configuration
client = ShadowOS(
api_key="sk-shadow-…", # or $SHADOW_OS_API_KEY
base_url="https://…", # or $SHADOW_OS_BASE_URL — for self-hosting or staging
timeout=120.0, # per request; generous on purpose (the service can cold-start)
max_retries=3, # timeouts, connection errors, 429 and 5xx
)
Bring your own httpx client (proxies, custom TLS, shared pools):
import httpx
client = ShadowOS(http_client=httpx.Client(proxies="http://…", timeout=60))
Retries use exponential backoff with full jitter. Retry-After always wins. 4xx responses other than 429
are never retried — they will not become a 200.
Development
pip install -e ".[dev]"
pytest
MIT licensed.
Metadata
Release files for shadow-os 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| shadow_os-1.0.0.tar.gz | 23.5 kB | With provenance | Details for shadow_os-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| shadow_os-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for shadow_os-1.0.0-py3-none-any.whl |
Total release size: 47.2 kB
Release files / shadow_os-1.0.0.tar.gz
| Download URL | shadow_os-1.0.0.tar.gz |
|---|---|
| Size | 23.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6f6bfa0a66bc441d87de41d5f043ceabb30ee6cc56ec24448716b2887adcf33d
|
|
BLAKE2b-256 checksum How to use checksums |
393998b1b2b4f8d95b7e0274d9e5ce93821c6645f9f8b95ead3af5c987cf6c61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / shadow_os-1.0.0-py3-none-any.whl
| Download URL | shadow_os-1.0.0-py3-none-any.whl |
|---|---|
| Size | 23.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
91441c29bd3ccc8adb61f7f43494c2d34fbf83ac28dd14172580d91084dbf93b
|
|
BLAKE2b-256 checksum How to use checksums |
7b364ba603be875f6e877ecb61d940df0ec1dbb9f2f8538b4fcc31c11988dd55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logSecurity for shadow-os 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for shadow_os-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.