Skip to main content

Python SDK for the AtlaSent authorization API

Project description

AtlaSent Python SDK

Execution-time authorization for AI agents. One call before a sensitive action runs. Fail-closed by design — no action proceeds without an explicit, verified permit.

pip install atlasent
# offline audit-bundle verification (Ed25519):
pip install "atlasent[verify]"

Quickstart

from atlasent import protect

permit = protect(
    agent="deploy-bot",
    action="production.deploy",
    context={"commit": commit, "approver": approver},
)
# If we got here, the action is authorized end-to-end.
# Otherwise protect() raised and the action never ran.

Set ATLASENT_API_KEY in the environment, or call atlasent.configure(api_key=...). That's the whole setup.

The protect() contract

atlasent.protect() is the category primitive. On allow, it returns a verified Permit. On anything else, it raises:

Outcome Raises
Policy DENY AtlaSentDeniedError
Permit failed verification AtlaSentDeniedError
HTTP 401 / 403 / 4xx / 5xx AtlaSentError (with .code)
Timeout / network failure AtlaSentError (code="timeout" / "network")
Rate limit (429) RateLimitError (subclass of AtlaSentError, .retry_after)

There is no permitted=False return path to forget. The action cannot execute unless a Permit is in hand.

from atlasent import protect, AtlaSentDeniedError, AtlaSentError

try:
    permit = protect(agent=agent, action=action, context=context)
    # Run the action. permit.permit_id + permit.audit_hash go in your log.
except AtlaSentDeniedError as exc:
    # Policy said no. exc.decision, exc.reason, exc.evaluation_id.
    log.warning("Denied: %s (evaluation_id=%s)", exc.reason, exc.evaluation_id)
except AtlaSentError as exc:
    # Transport / auth / server failure. exc.code, exc.status_code.
    log.error("AtlaSent unavailable: %s", exc)

AtlaSentDeniedError subclasses AtlaSentDenied, so except AtlaSentDenied: still catches protect() denials. Use except AtlaSentDeniedError: when you need to distinguish a policy decision from a transport error.

Async

from atlasent import AsyncAtlaSentClient

async with AsyncAtlaSentClient(api_key="ask_live_...") as client:
    permit = await client.protect(
        agent="clinical-data-agent",
        action="modify_patient_record",
        context={"user": "dr_smith", "patient_id": "PT-001"},
    )

Full feature parity with the sync surface — same return type, same exceptions, same fail-closed contract.

What a Permit gives you

@dataclass(frozen=True)
class Permit:
    permit_id: str     # opaque decision id (use for audit lookup)
    permit_hash: str   # verification hash bound to the permit
    audit_hash: str    # hash-chained audit-trail entry (21 CFR Part 11)
    reason: str        # policy engine's explanation
    timestamp: str     # ISO 8601 of the verification

Log permit_id + audit_hash for every action your code performs — they're the two fields a regulator or support ticket will ask for.

Framework integration

FastAPI

from fastapi import FastAPI, HTTPException
from atlasent import AsyncAtlaSentClient, AtlaSentDeniedError, AtlaSentError

app = FastAPI()
client = AsyncAtlaSentClient(api_key="ask_live_...")

@app.post("/modify-record")
async def modify_record(patient_id: str, agent_id: str):
    try:
        permit = await client.protect(
            agent=agent_id,
            action="modify_patient_record",
            context={"patient_id": patient_id},
        )
    except AtlaSentDeniedError as exc:
        raise HTTPException(403, detail=exc.reason) from None
    except AtlaSentError as exc:
        raise HTTPException(503, detail=str(exc)) from None
    return {"permit_id": permit.permit_id, "audit_hash": permit.audit_hash}

Flask

from flask import Flask, jsonify, abort, request
from atlasent import AtlaSentClient, AtlaSentDeniedError, AtlaSentError

app = Flask(__name__)
client = AtlaSentClient(api_key="ask_live_...")

@app.post("/modify-record")
def modify_record():
    try:
        permit = client.protect(
            agent="flask-agent",
            action="modify_patient_record",
            context={"patient_id": request.json["patient_id"]},
        )
    except AtlaSentDeniedError as exc:
        abort(403, description=exc.reason)
    except AtlaSentError as exc:
        abort(503, description=str(exc))
    return jsonify(permit_id=permit.permit_id, audit_hash=permit.audit_hash)

Decorator shortcuts — atlasent_guard for sync views, async_atlasent_guard for async ones — remain available for the pre-protect() gate() + GateResult idiom. See examples/fastapi_integration.py and examples/flask_integration.py.

configure()

import atlasent

atlasent.configure(
    api_key="ask_live_...",               # else reads ATLASENT_API_KEY
    base_url="https://api.atlasent.io",   # default
)

Or pass the same settings to AtlaSentClient(...) / AsyncAtlaSentClient(...) directly for per-client configuration:

from atlasent import AtlaSentClient

client = AtlaSentClient(
    api_key="ask_live_...",
    base_url="https://api.atlasent.io",  # default
    timeout=10,                          # seconds, default
    max_retries=2,                       # on 5xx / timeouts, default
    retry_backoff=0.5,                   # seconds, doubles each retry
)

Canonical SDK surface

Three primitives, each with a distinct mental model. New code should pick one of these:

Primitive Use when Lifecycle
protect() You want fail-closed execution: "no permit, no execution." Evaluate + verify in one call. Returns a Permit on allow; raises on deny / hold / escalate / verification failure / transport error.
evaluate() You need to inspect the four-value decision (allow / deny / hold / escalate). Returns the raw decision object. Does not collapse hold and escalate into a deny; does not auto-verify.
verify() You hold a permit token from a prior evaluate() and want to confirm it before execution. Distinct from the other two — operates on an existing permit.
from atlasent import protect, evaluate, verify

Deprecated convenience wrappers

These exist for backward compatibility and will be removed in atlasent v3. They emit DeprecationWarning on use.

  • authorize(agent, action, context) — data-not-exception variant (returns permitted: bool). Migrate to protect() for the fail-closed contract, or evaluate() if you specifically want to inspect the decision without raising.
  • gate(action, agent, context) — evaluate + verify, returning an inspectable GateResult. Migrate to protect() if you want fail-closed semantics, or evaluate() + verify() if you want the two-step inspectable shape.

There is no gate(...) lifecycle that protect() cannot express fail-closed, and there is no authorize(...) lifecycle that evaluate() cannot express by inspection.

Design choices

  • Fail-closed by construction. protect() either returns a Permit or raises. No ambiguous return values, no silent permits.
  • Sync + async feature parity. Every public method exists on both AtlaSentClient and AsyncAtlaSentClient.
  • Wire-compatible with the TypeScript SDK. A permit issued by one SDK verifies from the other.
  • PEP 561 typed. Ships a py.typed marker; every public function and type is annotated.

API endpoints

The SDK calls:

  • POST https://api.atlasent.io/v1-evaluate
  • POST https://api.atlasent.io/v1-verify-permit

Override with the base_url argument.

Requirements

  • Python 3.10+ (for str | None unions and datetime.UTC).
  • httpx >= 0.24, pydantic >= 2.0.

Related

  • TypeScript SDK: ../typescript/. Same wire contract, same fail-closed philosophy, same protect() verb.
  • Shared contract: ../contract/ — schemas, vectors, and the CI drift detector that keeps both SDKs honest.

Get an API key

Sign up at atlasent.io → Settings → API Keys.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

atlasent-2.5.0.tar.gz (184.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

atlasent-2.5.0-py3-none-any.whl (125.2 kB view details)

Uploaded Python 3

File details

Details for the file atlasent-2.5.0.tar.gz.

File metadata

  • Download URL: atlasent-2.5.0.tar.gz
  • Upload date:
  • Size: 184.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for atlasent-2.5.0.tar.gz
Algorithm Hash digest
SHA256 917d52bf3f9c87d61b6d37bb4623100e2b2195d59e1f182e6b7380bc0c2ef9cf
MD5 caf7f17447376bf56c4ccb158fd6777c
BLAKE2b-256 f4e1f4e9ce6d4cb1ade6d2436066c6cf18ba4211008b50cb828870a4c096d2f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlasent-2.5.0.tar.gz:

Publisher: publish-pypi.yml on AtlaSent-Systems-Inc/atlasent-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file atlasent-2.5.0-py3-none-any.whl.

File metadata

  • Download URL: atlasent-2.5.0-py3-none-any.whl
  • Upload date:
  • Size: 125.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for atlasent-2.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d166c2ffc35a8a06ccdfd7474c047c4653a62ee3ebc92c7e0b7673ae729442e
MD5 29896267a65991b6c0c3fe7097ff142d
BLAKE2b-256 442deac6e45a743cf68d639a0bf1847905b094c29c277e8297f1814e7a260963

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlasent-2.5.0-py3-none-any.whl:

Publisher: publish-pypi.yml on AtlaSent-Systems-Inc/atlasent-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page