CydraLabs

Documentation

Python agent SDK

cydra-sdk puts CydraGateway in front of your agent's tool calls: it authenticates the agent, submits each proposed action, waits for a human approval when policy requires one, and resubmits the identical request with the single-use approval token.

Overview

The gateway, not the agent, decides. The SDK never bypasses a decision: it submits, waits for approval when told to, and returns the decision and result. Everything it does is also possible with plain HTTP; see the API reference.

Package facts
Distribution / importcydra-sdk / cydra_sdk, version 0.1.0
Python3.10 or later
Dependencieshttpx (0.28)
StyleSynchronous; usable as a context manager. No background threads.

Installation

Install from the package you received
python -m pip install ./cydra-sdk          # a directory or .whl file

You need a registered agent and a workload identity (client ID and secret). The quick start shows how to create both. Keep the secret in your secret manager or environment, never in source code.

Submitting an action

Submit, inspect the decision, act on it
import os
from cydra_sdk import CydraGateway

with CydraGateway(
    "https://app.cydralabs.com",
    os.environ["CYDRA_CLIENT_ID"],
    os.environ["CYDRA_CLIENT_SECRET"],
) as gateway:
    result = gateway.request_action(
        tool="ledger.accounts.read",
        target="ledger://accounts/operating",
        parameters={"account": "operating"},
        justification="Month-end balance check",
    )
    print(result.state, result.decision, result.reason_codes)
    if result.executed:
        print(result.output)

request_action returns an ActionResult for every decision, including a deny: check result.decision or result.state. It raises an exception when the request itself fails (for example a validation error or an expired credential).

Waiting for approval

When policy requires approval, the initial call returns state == "approval_required" and nothing has run. authorize_and_execute handles the whole flow:

from cydra_sdk import ActionDenied, ApprovalNotGranted, ApprovalTimeout

try:
    result = gateway.authorize_and_execute(
        tool="payments.transfer.create",
        target="treasury://payments/supplier-run",
        parameters={"amount": 4800, "currency": "GBP", "beneficiary": "Harbor Logistics Ltd"},
        justification="Supplier run 2026-W40",
        wait_for_approval_seconds=900,   # 0 (the default) returns immediately without waiting
    )
except ActionDenied as exc:
    print("Denied:", exc.result.reason_codes)
except (ApprovalNotGranted, ApprovalTimeout) as exc:
    print("Not approved:", exc)
else:
    print(result.state, result.output)   # "executed", {"payment_reference": "PAY-…", ...}
  1. Submits the action with a generated idempotency key.
  2. If approval is required and wait_for_approval_seconds is above zero, polls the action's status (every poll_seconds, default 3) until an approval token is available.
  3. Resubmits the byte-for-byte identical request with the same key and the token. The token is bound to a hash of the request, so nothing can change in between.
  4. Raises ActionDenied if the decision is deny (or the action was denied or revoked); otherwise returns the result. Other outcomes, such as failed, are returned: check result.executed.

To control each step yourself, use request_action, then wait_for_approval(action_id), then request_action(..., idempotency_key=initial.idempotency_key, approval_token=token) with the same arguments.

Authentication

  • The SDK exchanges the client ID and secret at POST /api/v1/identities/token on its initial call (not in the constructor) and caches the token in memory.
  • It refreshes the token token_refresh_margin_seconds (default 30) before it expires. Tokens last five minutes.
  • When an action submission returns 401 with token_expired or session_revoked, it fetches a fresh token and retries once. A revoked identity fails again with AuthenticationError.
  • Every request carries an X-Correlation-ID beginning sdk-, which appears in the audit trail and evidence.

Idempotency

Every submission carries an Idempotency-Key. If you do not pass one, the SDK generates sdk-<32 hex characters> and returns it as result.idempotency_key. Resubmitting the same body with the same key returns the stored result instead of running the action twice, which makes retries after network errors safe. Pass your own key (for example derived from a job ID) when your agent may restart between attempts.

Reference

CydraGateway

CydraGateway(base_url: str, client_id: str, client_secret: str, *,
             http: httpx.Client | None = None,
             token_refresh_margin_seconds: int = 30)
CydraGateway methods
MethodBehaviour
request_action(*, tool, target, parameters=None, operation="invoke", on_behalf_of=None, justification=None, environment=None, idempotency_key=None, approval_token=None)Submits to POST /api/v1/actions and returns an ActionResult.
status(action_id)Returns the gateway status as a dict: {"id", "state", "decision", "approval": {"status", "expires_at", "approval_token"}}.
wait_for_approval(action_id, *, timeout_seconds=900, poll_seconds=3)Polls until an approval token is available and returns it; raises ApprovalNotGranted or ApprovalTimeout.
authorize_and_execute(*, tool, target, parameters=None, operation="invoke", on_behalf_of=None, justification=None, wait_for_approval_seconds=0, poll_seconds=3)Submit, wait when required and allowed, resubmit with the token; raises ActionDenied on deny.
close()Closes the HTTP client, including one passed in through http=. Also called when a with block ends.
agent_uriThe agent's URI (for example agent://acme/sales-agent), set after the initial token exchange.

There is no timeout argument: the default client uses 15 seconds (5 to connect). To change timeouts, proxies or TLS settings, pass your own httpx.Client as http.

ActionResult

ActionResult fields
FieldMeaning
action_idID of the action request
statereceived, approval_required, approved, executed, failed, denied, rejected, expired, cancelled or revoked
decisionallow, deny or require_approval
reason_codesReason codes from the decision (and any rejection)
approval_id / approval_expires_atSet when approval is required
output / execution_statusThe redacted execution output and its status, once executed
idempotency_keyThe key used for the submission
rawThe full response body
executed / needs_approvalConvenience properties

Errors

Exceptions
ExceptionRaised whenUseful attributes
CydraErrorBase class; also any API error responsecode (the API error code), correlation_id
AuthenticationErrorThe token exchange is refused (wrong secret, revoked identity)code, correlation_id
ActionDeniedauthorize_and_execute received a denyresult (use result.reason_codes)
ApprovalNotGrantedThe approval was rejected, expired or cancelled, or the action endedresult
ApprovalTimeoutNo approval within timeout_secondsresult

Network errors from httpx (timeouts, connection failures) are not wrapped. Because submissions are idempotent, it is safe to retry them with the same idempotency_key.

Good practice

  • Create one CydraGateway per agent process and reuse it; it caches the token.
  • Treat every result as authoritative: run nothing yourself when the state is not executed. The gateway runs the tool with a scoped credential.
  • Pass justification: approvers see it, inspection checks it, and it is kept in the evidence chain.
  • Set on_behalf_of when the agent acts for a person who has delegated authority to it (recorded on the agent's Identities tab); without a matching delegation the gateway denies the action.
  • Log exc.correlation_id when a call fails: it identifies the request in CydraLabs support and audit logs.

Applies to the CydraLabs proof-of-concept platform. Last updated 4 October 2026. Questions or corrections: contact us.