Python SDK
tigby on PyPI – generated from the API's own contract, sync and async.
pip install tigby # or: uv add tigbyGenerated 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 startedcreate_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 onceThe 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