Skip to main content

Python SDK

A typed Python client for the operations an integration runs server-side — provisioning customers, managing agents, and streaming chat.

Who can do what​

TaskUserAgent ownerAdmin
Use the SDK with their own key✅✅✅
Use the provisioning and admin routes it wraps——✅

Installing​

No PyPI release yet; install from git:

pip install "git+https://github.com/VerinFast/dolly.git#subdirectory=sdks/python"

Authenticating​

The client takes an admin user API key, sent as X-Api-Key. Two constraints, both of which bite if ignored:

  • It must be a user key. The agent and provisioning routes need a real user behind the request.
  • That user must be a tenant admin. Provisioning and admin routes are gated on the role.

Mint it once in the browser — key creation needs a sign-in — and keep it server-side. It is effectively a master key for your organization: it belongs on your backend and nowhere near a browser. To put an agent in front of the public, use a share link instead.

See API keys.

Provisioning a customer​

The method most integrations reach for. One call creates the customer's agent, attaches your registered tools with that customer's credentials, and discovers each MCP server's catalogue:

from dolly_sdk import DollyClient

dolly = DollyClient(api_key, "https://agent.verinfast.com")
result = dolly.provisioning.create_customer(
tenant_id,
external_id="customer_42",
agent={"name": "Customer 42's agent"},
)
# Store result's agent id against your customer — it's how you route requests.

See Provisioning customers for the full shape, including the rejected list you should be reading.

Agents​

agent = dolly.agents.create(tenant_id, "An agent", instructions="…")
dolly.agents.get(agent.id)
dolly.agents.list(tenant_id)
dolly.agents.delete(agent.id) # also releases the sandbox and purges the workspace

Streaming a conversation​

async for event in client.chat.stream(
agent_id,
"Show me pictures of Sean in the winter",
session_id=session_id,
attachments=[Attachment.from_path("undated.jpg")],
mode="plan", # propose without acting; "go" to proceed
):
...

mode is the same Plan / Go control the app offers.

Building your UI without a live agent​

The SDK ships a fake transport that replays a scripted event stream through a real client — same events, same iteration — so you can build and test your chat UI with no agent, no network, and no token spend:

from dolly_sdk import AsyncDollyClient
from dolly_sdk.testing import fake_chat_transport

transport = fake_chat_transport([
{"type": "tool_call", "data": {"name": "search_photos"}},
{"type": "message", "data": {"content": "Found 3 photos."}},
{"type": "task_completed", "data": {"session_id": "ses_1"}},
])
client = AsyncDollyClient("uak_x", "https://agent.test", transport=transport)

For tests, the client takes any httpx transport, so httpx.MockTransport works directly.

Errors​

Every failure is a DollyError subclass, with the API's own reason on .detail:

from dolly_sdk import DollyConflictError

try:
dolly.agents.create(tenant_id, "dup")
except DollyConflictError as exc:
print(exc.status, exc.detail) # 409, "A tool named 'dup' already exists"

DollyAuthError (401/403), DollyConflictError (409), DollyNotFoundError (404), and DollyAPIError for anything else.

A 404 may mean "not yours"

Some admin routes deliberately answer 404 rather than 403, so existence isn't disclosed to a caller who lacks access. Don't treat a 404 as proof something doesn't exist.

Not yet covered​

Managing the tool registry from the SDK — registering MCP servers, editing the egress allow-list — isn't in this release. Use Organization → Integrations or the custom-tools API meanwhile. See Custom tools.