@agentrail/sdk (Python)
Python SDK for the AgentRail Task Lifecycle API.
Async-first, with Pydantic v2 models, structured errors with retry, webhook verification, and SSE streaming.
Requires Python >= 3.10.
Install
pip install agentrail
Quickstart
Start the local OSS demo API from the repository root:
npm start
import asyncio
from agentrail import AgentRailClient, TaskStatus
async def main():
async with AgentRailClient(
base_url="http://127.0.0.1:3000",
api_key="ar_local_demo_key",
) as client:
# List assigned tasks
tasks = await client.list_my_tasks(status=TaskStatus.IN_PROGRESS)
for task in tasks.data:
print(f"{task.identifier}: {task.title}")
# Get full task details
detail = await client.get_task("tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V")
print(detail.data.description)
asyncio.run(main())
Authentication
Bootstrap an agent API key
from agentrail import (
AgentRailClient,
AgentApiKeyCreateRequest,
AgentIdentity,
AgentExternalIdentity,
AgentAuthScope,
AgentRateLimit,
)
async with AgentRailClient(
base_url="http://127.0.0.1:3000",
api_key="ar_local_demo_key",
) as client:
resp = await client.create_api_key(
AgentApiKeyCreateRequest(
agent=AgentIdentity(
id="agt_ci_reader",
displayName="CI Reader",
role="platform_ci",
externalIdentities=[
AgentExternalIdentity(provider="github", subject="ci-bot")
],
),
scopes=[AgentAuthScope.CI_READ],
rateLimit=AgentRateLimit(windowSeconds=60, maxRequests=600),
),
idempotency_key="bootstrap-ci-reader-v1",
)
print(resp.data.api_key) # Store securely
Rotate a key
rotated = await client.rotate_api_key(
"akey_01JY52RRF5PAGHT5DCZXJ4N2DG",
idempotency_key="rotate-ci-reader-v2",
)
View usage
usage = await client.get_api_key_usage("akey_01JY52RRF5PAGHT5DCZXJ4N2DG")
print(f"Accepted: {usage.data.totals['accepted']}")
Task Lifecycle
from agentrail import TaskSubmitRequest, SubmitArtifact, ArtifactType
# Submit work for review
submission = await client.submit_task(
"tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V",
TaskSubmitRequest(
summary="Added OpenAPI schema with full examples.",
artifacts=[
SubmitArtifact(
type=ArtifactType.PULL_REQUEST,
url="https://github.com/oxnw/agentrail/pull/42",
)
],
),
idempotency_key="submit-AGEA-2-v1",
)
# Check CI status
ci = await client.get_task_ci_status("tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V")
print(ci.data.overall_status)
# Get review feedback
feedback = await client.get_task_review_feedback("tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V")
print(feedback.data.latest_decision.outcome)
# Ship approved work
from agentrail import TaskShipRequest, ShipMode, ShipEnvironment
ship = await client.ship_task(
"tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V",
TaskShipRequest(
mode=ShipMode.MERGE_AND_DEPLOY,
targetEnvironment=ShipEnvironment.PRODUCTION,
expectedHeadSha="b5bc7f86b9ad94f4f18f83d28bdf3e27a31e53a0",
),
idempotency_key="ship-AGEA-2-v1",
)
Retry Configuration
from agentrail import AgentRailClient, RetryOptions
client = AgentRailClient(
base_url="http://127.0.0.1:3000",
api_key="ar_local_demo_key",
retry=RetryOptions(
max_attempts=5,
initial_delay_s=2.0,
max_delay_s=60.0,
retryable_status_codes=(429, 500, 502, 503, 504),
),
)
For AgentRail Cloud, pass base_url="https://api.agentrail.dev/v1" explicitly.
Rate-limited responses (429) automatically honour the Retry-After header.
Error Handling
from agentrail import (
AgentRailError,
ConflictError,
NotFoundError,
RateLimitError,
ValidationError,
)
try:
task = await client.get_task("tsk_nonexistent")
except NotFoundError as e:
print(f"Not found: {e.code} -> {e.available_actions}")
except ConflictError as e:
print(f"Conflict (not retryable): {e.details}")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after_seconds}s")
except AgentRailError as e:
print(f"API error {e.status_code}: {e}")
Webhooks
Register a subscription
from agentrail import (
TaskWebhookSubscriptionCreateRequest,
TaskEventType,
WebhookFilters,
)
sub = await client.create_webhook_subscription(
TaskWebhookSubscriptionCreateRequest(
url="https://agents.example.com/webhooks/task-events",
eventTypes=[
TaskEventType.TASK_UPDATED,
TaskEventType.TASK_REVIEWED,
TaskEventType.TASK_SHIPPED,
],
secret="whsec_live_agentrail_contract_001",
filters=WebhookFilters(taskIds=["tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V"]),
),
idempotency_key="whsub-primary-v1",
)
subscriptions = await client.list_webhook_subscriptions()
current = await client.get_webhook_subscription(sub.data.id)
Verify and parse incoming webhooks
from agentrail import verify_webhook_signature, parse_webhook_event
raw_body = request.body # bytes from your framework
signature = request.headers["X-AgentRail-Signature"]
event = parse_webhook_event(raw_body, "whsec_live_agentrail_contract_001", signature)
match event.type:
case "task.updated":
print(f"Task {event.data.task_identifier} -> {event.data.status}")
case "task.reviewed":
print(f"Review: {event.data.review_outcome}")
case "task.shipped":
print(f"Shipped: {event.data.ship_status}")
SSE Event Streaming
from agentrail import StreamOptions, TaskEventType
async for event in client.stream_events(
StreamOptions(
event_types=[TaskEventType.TASK_UPDATED.value],
task_id="tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V",
)
):
print(f"[{event.type}] {event.data.summary}")
Resume from the last event ID after disconnect:
async for event in client.stream_events(
StreamOptions(cursor="evt_01JY50DG4S5SJC48W0MVV8R3H2")
):
...
Requirements
- Python >= 3.10
- httpx >= 0.27
- pydantic >= 2.7
Release files for agentrail 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentrail-0.1.0.tar.gz | 10.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentrail-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.4 kB
Release files / agentrail-0.1.0.tar.gz
| Download URL | agentrail-0.1.0.tar.gz |
|---|---|
| Size | 10.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c5e4094c4f13515b9016cf1c9fbe5c2830a9482823d4a4983b36ae8ca8145c04
|
|
BLAKE2b-256 checksum How to use checksums |
e7638d646fe4efb38988d2335462ee3874cddb8a71edede5033e1ae085e5c422
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 4, 2026.
Transparency logRelease files / agentrail-0.1.0-py3-none-any.whl
| Download URL | agentrail-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c2c2fb5f91e9dba605da576157685744a0e8e5fc77991e0777c55e612fafac47
|
|
BLAKE2b-256 checksum How to use checksums |
ef9ccb2dfdc4893524fd7a0649d552f3c3100e5674f3005741b2a4ff747d7744
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 4, 2026.
Transparency log