Python SDK
salesos — typed resources, retries, idempotency, pagination, OAuth helpers and webhook verification for Python 3.9+, sync and asyncio.
salesos is the official Python client. It mirrors the TypeScript SDK:
models are generated from the OpenAPI document and the
event catalog, and it implements the contracts in these guides for you —
retries and rate limits, idempotency keys,
pagination, errors, OAuth and
webhook signatures.
It needs Python 3.9 or newer and has one runtime dependency, httpx.
It ships type hints (py.typed) and works with mypy and pyright.
The package is not on PyPI yet. Until it is, install it from the SalesOS repository:
pip install ./packages/sdk-python.
Quick start
The async client has the same resources; every call is awaited and iterate() returns an async iterator:
| Option | Default | |
|---|---|---|
api_key | Sent as X-API-Key | |
access_token | Bearer token: a string, or a function (sync or async) returning the current one | |
auth | Any AuthProvider: ClientCredentialsAuth, OAuthTokenAuth, SessionAuth or your own | |
base_url | https://www.salesos.org | /v1 is added to paths that don't carry it |
organization_id | Sent as X-Organization-ID (session tokens with several memberships) | |
timeout | 30 | Seconds, per attempt |
max_retries | 2 | Extra attempts for retryable failures |
max_retry_delay | 30 | A longer Retry-After is not waited for; the error is raised |
auto_idempotency | True | Random Idempotency-Key on every POST/PATCH |
on_retry | Called with RetryInfo(attempt, delay, error) before each retry | |
http_client / transport | Bring your own httpx.Client / transport (proxies, tests) |
Resources
| Attribute | Methods |
|---|---|
leads, contacts, accounts, opportunities, tasks, quotes, orders, cases | list, iterate, pages, get, create, update, delete |
activities | the same without update (activities are immutable) |
custom_objects | definitions, plus .records(object_id) with list / iterate / get / create / update / delete |
webhooks | CRUD, activate, deactivate, rotate_secret, test, deliveries, iterate_deliveries, delivery, replay |
events | catalog(), get(type) (no credentials) |
bulk | create_job (upload or inline), list_jobs, get_job, cancel_job, results, failures, download_results, download_failures, wait_for_job |
custom_actions | CRUD, rotate_secret, test, run, invocations |
apps | runtime_installation() (your app's OAuth token); publishing and installs (session) |
metadata | types, retrieve, deploy, run_tests, list_deploys, get_deploy, check_again, cancel, wait_for_deploy, get_test_policy, set_test_policy |
functions | list, get, create, update, delete, create_version, deploy, rollback, set_secret, delete_secret, test, invoke, invocations, invocation |
api_usage | summary(days=…, principal_type=…, principal_id=…) |
event_stream | trigger(type) (streaming itself is in the CLI and the TypeScript SDK) |
marketplace | listings, iterate_listings, categories, listing, reviews (no credentials); directory, save_review (session); .publisher (submissions, domains, replies) |
Anything else: client.request("GET", "/pipelines", query={...}), or by operation id:
Every method takes options={"timeout": 5, "idempotency_key": "...", "headers": {...}, "max_retries": 0}.
Retries and idempotency
- GET, HEAD and OPTIONS are retried on network errors, timeouts, 408 and 5xx with jittered backoff.
- POST and PATCH carry an automatic
Idempotency-Key, the same on every attempt, so they are retried safely; a write without a key (auto_idempotency=Falseoroptions={"idempotency_key": False}) is never retried. Multipart uploads are never retried. - 429 is retried for any method when
Retry-Aftersays when; 503 honoursRetry-Aftertoo. 409 IDEMPOTENCY_REQUEST_IN_PROGRESS(a duplicate still running) is waited out.- The OAuth token endpoint is never retried: codes are single-use and a reused refresh token revokes its whole family.
Errors
Every failure raises SalesOSError: status (0 when no response arrived), code, message,
request_id, retry_after (seconds), headers and the parsed body.
OAuth
Client credentials (your app acting as its integration user) — tokens are fetched on first use and refreshed shortly before they expire:
Authorization code with PKCE (works for public clients without a secret):
Then let the SDK keep the token fresh. Refresh tokens rotate; persist each new set in on_tokens:
OAuthClient also has refresh, client_credentials, revoke, introspect and userinfo,
each with an a-prefixed async twin.
Bulk jobs
file is a path, bytes or a binary file object; pass inline CSV / JSON-lines as data= or rows
as records=. Exports: create_job("contact", "export", fields=["email", "firstName"]).
Webhooks
Verify deliveries against the raw body, before parsing JSON:
verify_webhook_signature(raw_body, headers, secret) returns a bool instead. Both check
X-SalesOS-Signature (t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>), reject
timestamps more than 300 seconds away (tolerance=), accept any matching v1 (secret rotation)
and compare in constant time. The SDK's tests run the server's own signer and check both
directions.
App backends
Models
salesos.models holds a TypedDict for every schema in the public OpenAPI document (Lead,
CreateLeadDto, Opportunity, …) and salesos.events the webhook event types and their data
shapes. They are generated; refresh them after a public API change:
Metadata, functions and usage
deploy(..., run_tests="NONE" | "SPECIFIED" | "ALL") picks which tests run first;
salesos.METADATA_TYPES lists every component type. AsyncSalesOS has the same methods. Streaming live events is in the CLI
(salesos listen) and the TypeScript SDK.
TypeScript SDK
@salesos/sdk — typed resources, retries, idempotency, pagination, OAuth helpers and webhook verification for Node 18+ and browsers.
CLI
salesos — sign in, call any endpoint, deploy and test metadata, listen to live events, work with functions, records, bulk jobs and developer orgs from the terminal.