Skip to content

SDK Reference (auto-generated)

This page is auto-generated from source docstrings via mkdocstrings. For narrative documentation with examples, see the SDK Guide.

Client

unitysvc_sellers.Client

Client(api_key: str, *, base_url: str | None = None, timeout: float | Timeout | None = 30.0, verify_ssl: bool = True)

Synchronous seller SDK client.

Parameters:

Name Type Description Default
api_key str

A seller API key (svcpass_...). Encodes the seller context, so no separate seller_id is required.

required
base_url str | None

Override the default base URL. If not provided, falls back to the UNITYSVC_SELLER_API_URL environment variable, then to :data:DEFAULT_SELLER_API_URL.

None
timeout float | Timeout | None

Per-request timeout in seconds. Default 30s.

30.0
verify_ssl bool

Whether to verify TLS certificates. Default True. Set to False only for local testing against a self-signed backend.

True

Attributes

services property

services: Services

promotions property

promotions: Promotions

groups property

groups: Groups

documents property

documents: Documents

secrets property

secrets: Secrets

tasks property

tasks: Tasks

Methods:

from_env classmethod

from_env(**kwargs: object) -> Client

Construct a client from environment variables.

Reads :data:ENV_SELLER_API_KEY (required) and :data:ENV_SELLER_API_URL (optional). Any extra keyword arguments are forwarded to the :class:Client constructor.

upload

upload(data_dir: str | Path, *, on_progress: Callable[[str, str, str, str], None] | None = None, name: str | None = None, auto_submit: bool = False) -> UploadResult

Upload services from a seller catalog directory.

Thin wrapper around :func:upload.upload_directory.

AsyncClient

unitysvc_sellers.AsyncClient

AsyncClient(api_key: str, *, base_url: str | None = None, timeout: float | Timeout | None = 30.0, verify_ssl: bool = True)

Asynchronous seller SDK client.

Parameters:

Name Type Description Default
api_key str

A seller API key (svcpass_...). Encodes the seller context, so no separate seller_id is required.

required
base_url str | None

Override the default base URL. If not provided, falls back to UNITYSVC_SELLER_API_URL, then to :data:unitysvc_sellers.DEFAULT_SELLER_API_URL.

None
timeout float | Timeout | None

Per-request timeout in seconds. Default 30s.

30.0
verify_ssl bool

Whether to verify TLS certificates. Default True.

True

Attributes

services property

services: AsyncServices

promotions property

promotions: AsyncPromotions

groups property

groups: AsyncGroups

documents property

documents: AsyncDocuments

secrets property

secrets: AsyncSecrets

tasks property

tasks: AsyncTasks

Methods:

from_env classmethod

from_env(**kwargs: object) -> AsyncClient

Construct an :class:AsyncClient from environment variables.

Resources

Secrets

unitysvc_sellers.secrets.Secrets

Secrets(client: AuthenticatedClient)

Operations on the seller's secrets (/v1/seller/secrets).

Methods:

list

list(*, skip: int = 0, limit: int = 100) -> SecretsPublic

List the seller's secrets (metadata only — values are never returned).

get

get(name: str) -> SecretPublic

Get metadata for a single secret by name.

delete

delete(name: str) -> None

Delete a secret by name. This action cannot be undone.

Services

unitysvc_sellers.services.Services

Services(client: AuthenticatedClient, *, parent: Client)

Operations on the seller's service catalog (/v1/seller/services).

Example::

# Active-record style — preferred for chained calls
for svc in client.services.list(limit=50):
    print(svc.id, svc.name, svc.status)
    if svc.status == "draft" and svc.is_complete:
        svc.submit()

# Or the equivalent manager-style — by id
client.services.update(service_id, {"status": "pending"})
client.services.delete(service_id)

Methods:

list

list(*, cursor: str | None = None, limit: int = 50, status: str | None = None, visibility: str | None = None, service_type: str | None = None, listing_type: str | None = None, name: str | None = None, provider: str | None = None, ids: list[UUID] | None = None) -> ServiceList

List services owned by the authenticated seller.

Cursor-paginated. Returns a :class:ServiceList that's iterable over :class:Service items and exposes next_cursor / has_more for manual pagination. For "iterate all pages" use :meth:iter_all.

iter_all

iter_all(*, limit: int = 50, status: str | None = None, visibility: str | None = None, service_type: str | None = None, listing_type: str | None = None, name: str | None = None, provider: str | None = None) -> Iterable[Service]

Iterate over all services across pages, auto-advancing the cursor.

Convenience for the common "process everything in my catalog" loop — wraps :meth:list + next_page so callers don't thread cursors manually.

get

get(service_id: str | UUID) -> Service

Get the full record for a single service.

Returns a :class:Service wrapping :class:ServiceDetailResponse — includes documents and interfaces alongside the bare service fields.

run_tests

run_tests(service_id: str | UUID, *, document_id: str | None = None, force: bool = False, poll_interval: float = 2.0, timeout: float = 600.0, on_progress: Callable[[str], None] | None = None) -> RunTestsResult

Queue a server-side diagnostic and block until it completes.

Calls POST /v1/seller/services/{id}/run-tests to queue a run_service_diagnostic celery task on the backend, then polls GET /v1/seller/tasks/{task_id} until the task reaches a terminal state. Returns the parsed task payload as a :class:RunTestsResult.

The diagnostic runs every executable document (connectivity tests + code examples) across every active access interface inside the cluster — the same network environment customers hit — and falls back to an upstream-mode probe on any iface-level gateway failure so the result attributes the fault as platform_fault vs upstream_fault.

Parameters:

Name Type Description Default
service_id str | UUID

UUID of the service to test.

required
document_id str | None

When set, restrict execution to one document.

None
force bool

Re-execute documents whose per-iface result on meta.test.tests[iface_id].status was previously success. Default skips them.

False
poll_interval float

Seconds between task-status polls.

2.0
timeout float

Hard cap on total wait, including queue time.

600.0
on_progress Callable[[str], None] | None

Optional callback on_progress(status: str) called every poll tick with the current task status string ("pending", "running", …). Use for a spinner / progress indicator.

None

Returns:

Type Description
RunTestsResult

class:RunTestsResult with per-(doc × iface) rows on

RunTestsResult

.results and pass/fail counts on .success_count /

RunTestsResult

.fail_count / .skipped_count. .passed is the

RunTestsResult

convenience boolean.

upload

upload(body: BodyServicesUpload | dict[str, Any], *, auto_submit: bool = False) -> TaskQueuedResponse

Submit a service for ingestion.

body is {"data": {provider_data, offering_data, listing_data}, "service_status": {...}} — the authored content and the status sidecar (service.json) travel as separate fields.

With auto_submit=True the freshly published draft is also submitted for review (validate → pending → run tests) in the same ingest task; otherwise (the default) it is left as a reviewable draft.

update

update(service_id: str | UUID, body: dict[str, Any]) -> ServiceUpdateResponse

Update a service — status, visibility, routing vars, and/or list price.

All fields are optional. Include only the fields you want to change. Multiple fields can be updated in a single request.

Parameters:

Name Type Description Default
service_id str | UUID

Service to update.

required
body dict[str, Any]

Dict with any combination of::

{"status": "pending"} {"visibility": "public"} {"routing_vars": {"region": "us-east"}} # full replacement {"routing_vars": {"set": {"count": 1}}} # partial update {"list_price": {"type": "constant", "price": "1"}} # full replacement {"list_price": {"set": {"price": "2"}}} # partial update

required

Example::

# Set visibility and update price in one call
client.services.update(service_id, {
    "visibility": "public",
    "list_price": {"type": "constant", "price": "1.00"},
})

submit_for_review

submit_for_review(service_id: str | UUID) -> ServiceUpdateResponse

Submit a service for review (draft / rejected / suspendedpending) and run the activation test pipeline.

Calls POST /v1/seller/services/{id}/submit. Unlike a plain update({"status": "pending"}) (a pure "mark pending" status change), this validates the content and dispatches the gateway test job that drives the service to review / active / rejected.

  • Duplicate content ⇒ a 200 no-op (status unchanged) — the response message explains why; idempotent re-submits don't churn the catalog.
  • Invalid content ⇒ raises (HTTP 400).

delete

delete(service_id: str | UUID, *, dryrun: bool = False) -> ServiceDeleteResponse

Delete a service.

Promotions

unitysvc_sellers.promotions.Promotions

Promotions(client: AuthenticatedClient, *, parent: Client)

Operations on the seller's promotions (/v1/seller/promotions).

Example::

# Active-record style — preferred for chained calls
for promo in client.promotions.list():
    print(promo.code, promo.status)
    if promo.status == "active":
        promo.update({"status": "paused"})

# Or the equivalent manager-style — by id
client.promotions.update(promotion_id, {"status": "paused"})
client.promotions.delete(promotion_id)

Methods:

list

list(*, cursor: str | None = None, limit: int = 50, status: PriceRuleStatusEnum | str | None = None) -> PromotionList

List the seller's promotions.

Cursor-paginated. Returns a :class:PromotionList that's iterable over :class:Promotion items and exposes next_cursor / has_more for manual pagination. For "iterate all pages" use :meth:iter_all.

iter_all

iter_all(*, limit: int = 50, status: PriceRuleStatusEnum | str | None = None) -> Iterable[Promotion]

Iterate over all promotions across pages, auto-advancing the cursor.

get

get(promotion_id: str | UUID) -> Promotion

Get a single promotion by id.

upsert

upsert(body: SellerPromotionCreate | dict[str, Any]) -> Promotion

Create or update a promotion by name (idempotent).

If a promotion with the same name already exists for this seller it is updated in place; otherwise a new one is created. Use this for CLI-driven workflows where promotions are managed as files identified by name.

update

update(promotion_id: str | UUID, body: SellerPromotionUpdate | dict[str, Any]) -> Promotion

Patch a single promotion by id (partial update).

delete

delete(promotion_id: str | UUID) -> None

Delete a promotion. Returns nothing on success.

Groups

unitysvc_sellers.groups.Groups

Groups(client: AuthenticatedClient, *, parent: Client)

Operations on the seller's service groups (/v1/seller/service-groups).

Example::

# Active-record style — preferred for chained calls
for grp in client.groups.list():
    print(grp.name, grp.display_name)
    if grp.status == "draft":
        grp.update({"status": "active"})

# Or the equivalent manager-style — by id
client.groups.update(group_id, {"status": "active"})
client.groups.delete(group_id)

Methods:

list

list(*, cursor: str | None = None, limit: int = 50, status: ServiceGroupStatusEnum | str | None = None) -> GroupList

List the seller's service groups.

Cursor-paginated. Returns a :class:GroupList that's iterable over :class:Group items and exposes next_cursor / has_more for manual pagination. For "iterate all pages" use :meth:iter_all.

iter_all

iter_all(*, limit: int = 50, status: ServiceGroupStatusEnum | str | None = None) -> Iterable[Group]

Iterate over all groups across pages, auto-advancing the cursor.

get

get(group_id: str | UUID) -> Group

Get a single service group by id.

upsert

upsert(body: ServiceGroupCreate | dict[str, Any]) -> Group

Create or update a service group by name (idempotent).

update

update(group_id: str | UUID, body: ServiceGroupUpdate | dict[str, Any]) -> Group

Patch a single service group by id.

delete

delete(group_id: str | UUID) -> None

Delete a service group.

Documents

unitysvc_sellers.documents.Documents

Documents(client: AuthenticatedClient)

Operations on seller test documents (/v1/seller/documents).

Methods:

get

get(document_id: str | UUID) -> DocumentDetailResponse

Get the full record for a single document.

execute

execute(document_id: str | UUID, *, force: bool = False) -> DocumentExecuteResponse

Execute a code-example / connectivity-test document.

Parameters:

Name Type Description Default
document_id str | UUID

The document to execute.

required
force bool

If True, execute even if the document was previously marked skipped or has a recent successful run.

False

update_test

update_test(document_id: str | UUID, body: DocumentTestUpdate | dict[str, Any]) -> DocumentTestStatusResponse

Update a document's test state (skip / unskip / mark pass-fail).

Tasks

unitysvc_sellers.tasks.Tasks

Tasks(client: AuthenticatedClient)

Operations on Celery task status (GET /tasks/?id=…).

Methods:

get

get(*task_ids: str) -> dict[str, dict[str, Any]]

Return a mapping of task_id → status dict.

Accepts one or more task IDs. Uses GET /tasks/?id=a&id=b&… (up to 100 IDs per call).

wait

wait(*task_ids: str, timeout: float = 600.0, poll_interval: float = 2.0, on_update: Any = None) -> dict[str, dict[str, Any]]

Poll until all task_ids reach a terminal state, or time out.

Returns a mapping of task_id → final status dict.

on_update, if provided, is called as on_update(terminal_count, total_count, last_finished_ids) after every poll that advances the terminal-count watermark.

On timeout, the returned dict includes every task — terminal ones with their final status, still-running ones with their last-seen status.

Exceptions

unitysvc_sellers.exceptions

Exception hierarchy for the seller SDK.

All errors raised by unitysvc_sellers are subclasses of :class:SellerSDKError, which in turn is a subclass of Exception. This lets callers choose the granularity that fits their use case::

try:
    client.services.list()
except NotFoundError:
    ...
except SellerSDKError:
    ...

Classes

SellerSDKError

Bases: Exception

Base class for all errors raised by unitysvc_sellers.

APIError

APIError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: SellerSDKError

The server returned a non-success status code.

Attributes:

Name Type Description
status_code

HTTP status code.

detail

Parsed error payload from the server (usually a dict matching ErrorResponse), or the raw text if not JSON.

response_body

Raw response bytes, useful for debugging.

AuthenticationError

AuthenticationError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

401 Unauthorized. The API key is missing, invalid, or expired.

PermissionError

PermissionError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

403 Forbidden. The authenticated seller cannot perform this action.

NotFoundError

NotFoundError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

404 Not Found. The requested resource does not exist for this seller.

ValidationError

ValidationError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

400/422. The request body failed server-side validation.

detail typically contains a list of per-field errors.

ConflictError

ConflictError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

409 Conflict. The request conflicts with server state (e.g. duplicate name).

RateLimitError

RateLimitError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

429 Too Many Requests. Retry after the backoff window.

ServerError

ServerError(message: str, *, status_code: int, detail: Any = None, response_body: bytes | None = None)

Bases: APIError

5xx. The server encountered an error. Safe to retry idempotent requests.