SDK reference
The SDK — what'll be in your repo when the platform opens.
A typed Python and TypeScript SDK that handles auth, billing, GDPR consent,
observability, async compute, workflows, secrets, and custom domains — the
boring-but-critical production layer. Proven inside Sulis's own stack today; the public
interface ships with the platform.
Preview · ships with the platform
Three things to know about how the SDK works
Same trust position as the rest of Sulis: your code stays yours, the integration shape stays familiar, and you can leave when it stops being useful.
01
The SDK is local code in your repo, not a hosted API call.
Your app installs sulis (Python) or @sulis/sdk (TypeScript) as a normal package. The SDK source lives in your node_modules / virtualenv — you can read it, vendor it, patch it. The SDK talks to your dedicated Sulis backend over HTTPS, but the integration surface is code you control.
02
Auth via your API key + your platform's identity model.
Same shape Stripe and Clerk use. Four key types (organisation, user, service, JWT) so you can match scope to caller. Keys roll without code changes — the SDK just reads them from your environment.
03
Designed so you can swap us out without a rewrite.
When you use the Sulis Plugin + SDK together, the plugin writes your code following Ports and Adapters (sometimes called the Plugin pattern). The Sulis SDK becomes one adapter behind a port your code calls — swap in a different adapter later and your app code doesn't change. We earn the right to be useful. We don't lock you in.
Quickstart
The same operation, in both languages:
Python
from sulis.sdk.platform import SulisClient
client = SulisClient(api_key="okey_...")
platform = await client.platforms.create(
name="My Platform",
display_name="My Platform",
)
await client.invitations.create(
platform_id=platform.id,
email="founder@example.com",
)
TypeScript
import { SulisClient } from "@sulis/sdk";
const client = new SulisClient({ apiKey: "okey_..." });
const platform = await client.platforms.create({
name: "My Platform",
displayName: "My Platform",
});
await client.invitations.create({
platformId: platform.id,
email: "founder@example.com",
});
Both SDKs are generated from the same server-side action registry, so the surface stays in
lockstep. Idiomatic in both languages — snake_case in Python, camelCase in
TypeScript, with the same operations and the same typed responses.
The 9 domain groups
The SDK's 24 modules are organised into 9 conceptual groups. Each group is a coherent slice
of the production layer — pick the ones your product needs, ignore the rest.
01 · 5 modules
Identity
IAM · Users · API Keys · Memberships · Invitations
The model of who's allowed to do what, and how you let new people in.
02 · 2 modules
Platform structure
Platforms · Organizations
Tenants and the teams inside them.
03 · 2 modules
Data
Entities · Secrets
Structured storage and encrypted secret storage.
04 · 3 modules
Compute
Compute · Manifests · Builds
Services, jobs, deployments. Manifests as the declarative shape.
05 · 2 modules
Commercial
Billing · Catalog
Subscriptions, invoicing, usage-based billing. Products and pricing.
06 · 3 modules
Content + AI
Content · Studios · Agents
Content pipelines, structured prompting environments, agent operations.
07 · 2 modules
Async
Workflows · Scheduling
Long-running orchestrations and scheduled tasks.
08 · 3 modules
Messaging
Notifications · Communication · Requests
Multi-channel notifications, email delivery, outbound HTTP.
09 · 2 modules
Operations
Observability · Networking
Events, metrics, DNS, TLS, load balancers.
The 24 modules
One row per module. Every module is in active build inside Sulis's own stack today.
iamIdentity and access management primitives.
usersUser profiles, roles, lifecycle.
api_keysCreate, rotate, scope API keys.
membershipsUser-organisation relationships.
invitationsInvite users to platforms or organisations.
platformsCreate, list, update platform tenants.
organizationsTeams within platforms.
entitiesRDF-style entity storage (StorageDomain).
secretsEncrypted secret storage, rotation, scoped access.
computeServices, jobs, deployments.
manifestsApply / reconcile / delete stored manifests across managed kinds.
buildsContainer builds via Cloud Build.
billingPlans, invoicing, usage-based billing.
catalogProducts, service definitions, commercial enablement.
contentContent briefs, artifacts, gates, deployment pipeline.
studiosAI studio definitions — structured prompting environments.
agentsAgent operations and interactions.
workflowsAsync workflow execution and monitoring.
schedulingCron, delayed, and recurring tasks.
notificationsMulti-channel — email, SMS, push, Slack, WhatsApp, in-app.
communicationEmail delivery and template management.
requestsOutbound HTTP with retry, pagination, monitoring.
observabilityEvents, usage tracking, logs.
networkingDNS zones, TLS certificates, load balancers.
The auth model
Four key types match scope to caller. Pick whichever is right for the context you're calling
from; the SDK figures out the rest.
okey_…
Organisation key. Acts on behalf of a specific org. The most common shape for server-side product code.
ukey_…
User key. Acts as a specific user. Useful for impersonation or per-user automations.
skey_…
Service key. Cross-org service-to-service calls — narrow scopes, no human identity.
JWT
Session token. For browser-side or short-lived calls where the SDK runs in a user's session.
The SDK reads SULIS_API_KEY from the environment by default, so most apps never
hard-code keys. Switch keys by switching env vars — no code changes.
The error model
Every SDK call can raise one of five typed exceptions. Same shape in both languages.
AuthenticationErrorThe API key is missing, malformed, or revoked.
NotFoundErrorThe resource you asked for doesn't exist (or isn't visible to your key's scope).
ValidationErrorYour inputs failed server-side validation. Includes .field_errors for per-field messages.
RateLimitErrorYou've hit a rate limit. Includes .retry_after seconds. The SDK retries transient cases automatically.
APIErrorAnything else — the base class. Includes HTTP status code and server message.
All list endpoints support auto-pagination — iterate with async for in Python or
for await in TypeScript and the SDK handles the page-walking for you.
When the SDK opens
Today the SDK runs inside Sulis's own stack — it's how Sulis itself ships features. When the
platform opens to public install, this page expands into a full per-module reference,
matching the shape of the Plugin reference: every module
gets its own page, with method signatures, request/response examples, edge cases, and
migration notes between versions.
The SDK is under active development, and feedback is genuinely welcome.
If there are operations you wish existed, shapes you'd want changed, or use cases you
don't see covered here,
message me on LinkedIn.
Alpha-tester input directly shapes what ships when the SDK opens.
Back to
Docs overview
→