SDKs

Python SDK

tigby on PyPI – generated from the API's own contract, sync and async.

pip install tigby        # or: uv add tigby

Generated from the OpenAPI document, which is itself generated from the running API – the package cannot describe an endpoint the API does not have.

Use it

import os

from tigby import TigbyError, create_client
from tigby.api_client.api.identities import create_identity, list_identities
from tigby.api_client.models import CreateIdentityRequest

client = create_client(api_key=os.environ["TIGBY_API_KEY"])

created = create_identity.sync(client=client, body=CreateIdentityRequest(handle="orderbot"))
print(created.saga.status)  # provisioning has started

create_async_client gives the same client for the awaitable half of every operation (asyncio, asyncio_detailed). Both default to https://api.tigby.eu; pass base_url="http://localhost:8080" for a dev stack.

The key is optional, and the credential goes on per operation rather than onto every request. The operations tigby.PUBLIC_OPERATIONS lists take none, and one of them is how a key is obtained:

import time

from tigby import TigbyError, create_client
from tigby.api_client.api.auth import poll_device_authorization, start_device_authorization
from tigby.api_client.models import DeviceTokenRequest, StartDeviceRequest

anonymous = create_client()

started = start_device_authorization.sync(client=anonymous, body=StartDeviceRequest(name="laptop", client="cli"))
print(started.user_code)  # confirm this at started.verification_uri
interval = max(1, started.interval_seconds)

while True:
    time.sleep(interval)
    try:
        issued = poll_device_authorization.sync(
            client=anonymous, body=DeviceTokenRequest(device_code=started.device_code)
        )
        break
    except TigbyError as error:
        if error.code == "slow_down":  # the API asking for more room
            interval += 1
        elif error.code != "authorization_pending":
            raise  # denied, expired, used or invalid: the flow is over

print(issued.key)         # the Admin Key, shown once

The poll is a loop because the API answers 200 only on the first poll after the person confirms, and refuses with authorization_pending until then. Poll at started.interval_seconds; started.expires_at is when the code stops being good.

Errors

Every refusal raises TigbyError – the RFC 9457 problem with its Tigby members:

try:
    create_identity.sync(client=client, body=CreateIdentityRequest(handle="orderbot"))
except TigbyError as error:
    error.code        # "handle_taken" – stable, branch on it
    error.agent_hint  # what to do instead
    error.doc_url     # where a human reads more

On this page