Python SDK — Getting Started
Python SDK — Getting Started
Section titled “Python SDK — Getting Started”sencai-sdk is a Python client for the Sencai Platform API (/api/v1/*), generated from the
public OpenAPI spec (openapi-generator-cli, python template — F4.DEVPORTAL.02). It lives at
sdk-python/ in the monorepo (a standalone pyproject.toml/setup.cfg package, not an npm
workspace member).
Installation
Section titled “Installation”sencai-sdk is not yet published to PyPI — public package distribution is a separate
business/release decision, out of scope for the SDK generation work itself. The package layout is
already PyPI-ready; until publication, install it from the monorepo path or directly from git:
# From the monorepo checkoutpython3 -m venv .venv.venv/bin/pip install -e ./sdk-python
# Directly from gitpip install "git+https://github.com/sencai-space/sdk-python.git"Initialize the client
Section titled “Initialize the client”from sencai_sdk import ApiClient, Configuration, OrganisationApi
config = Configuration( # Local dev: http://api.sencai.localhost/api/v1 or http://localhost:1337/api/v1 # Production: https://api.sencai.space/api/v1 host=os.environ["SENCAI_API_BASE_URL"], # Bearer JWT (Keycloak access token today) — or a future API-key header. The # client only provides the injection point; it does not fetch or refresh # the token for you. access_token=os.environ["SENCAI_ACCESS_TOKEN"],)
with ApiClient(config) as client: organisations = OrganisationApi(client)Configuration.host and access_token are always constructor parameters — the package never
hardcodes a production URL.
Complete example: list organisations, invite a member
Section titled “Complete example: list organisations, invite a member”Verified against a local run-local-dev.sh stack (backend, backend-db, auth profiles).
import osimport sencai_sdk
config = sencai_sdk.Configuration( host=os.environ["SENCAI_API_BASE_URL"], access_token=os.environ["SENCAI_ACCESS_TOKEN"],)
with sencai_sdk.ApiClient(config) as client: organisations = sencai_sdk.OrganisationApi(client)
# List organisations the caller belongs to. result = organisations.find_organisation(pagination_page_size=10) print([org.name for org in result.data]) # -> ['QA Admin Workspace', 'e2e-org-d18a54e2', 'e2e-org-d9e67836']
target_org = result.data[0]
# Invite an existing Strapi user into that organisation. `POST # /organisations/{id}/invite` requires the invitee to already have a # registered account (see the Error handling section below for what # happens otherwise) and the caller to be admin+ in the organisation. body = sencai_sdk.OrganisationInviteMemberRequest(email="new-member@example.com", role="viewer") invite = organisations.organisation_invite_member(target_org.document_id, body) print(invite.data) # -> OrganisationInviteMember200ResponseData(id=1264, attributes=OrganisationInviteMember200ResponseDataAttributes( # email='new-member@example.com', role='viewer', invited_by='you@example.com', # invited_at=datetime.datetime(2026, 7, 5, 11, 56, 34, tzinfo=TzInfo(0)), status='pending'))Error handling
Section titled “Error handling”Every non-2xx response raises sencai_sdk.exceptions.ApiException (or a status-specific
subclass, e.g. UnauthorizedException/ForbiddenException) with .status and a raw .body
string — the typed method never returns a partially-valid result on failure. Error bodies follow
Strapi’s standard envelope: { "error": { "status", "name", "message", "details" } }.
import jsonfrom sencai_sdk.exceptions import ApiException
try: organisations.organisation_invite_member(target_org.document_id, body)except ApiException as err: payload = json.loads(err.body)["error"] print(err.status, payload["name"], payload["message"]) # A real, commonly hit example — the organisation's plan seat limit: # 402 PaymentRequired "Member limit reached (3/3). Upgrade your plan." # payload["details"] -> {"limit": 3, "current": 3, "limit_source": "organisation_plan", ...}Other status codes you should expect from this endpoint specifically: 400 (missing/invalid
email/role, or no Strapi user registered with that email — this endpoint does not invite
not-yet-registered emails), 401 (missing/expired token), 403 (caller is not admin+ in the
organisation), 409 (that user is already a member).
SDK versioning & breaking changes
Section titled “SDK versioning & breaking changes”The package version (pyproject.toml/setup.py → version) is derived from the OpenAPI spec’s
info.version on every regeneration — it is never bumped by hand. A breaking API change ships as
a new spec major version, which regenerates this SDK as a new major version too. See
API versioning for what counts as breaking, the /api/v1/ →
/api/v2/ migration path, and the SUNSET_DATE deprecation-header convention — this page does
not repeat that policy, only how it surfaces in the generated client.
For the full generated reference of every operation this client exposes, see the API Reference.