Skip to main content

Alter Vault Python SDK - OAuth token management with policy enforcement

Project description

Alter SDK for Python

Official Python SDK for Alter Vault - OAuth token management with policy enforcement.

Features

  • Zero Token Exposure: Tokens are never exposed to developers - injected automatically
  • Single Entry Point: One method (vault.request()) for all provider APIs
  • Type-Safe Enums: Provider and HttpMethod enums with autocomplete
  • URL Templating: Path parameter substitution with automatic URL encoding
  • Automatic Audit Logging: All API calls logged with request metadata (HTTP method and URL) for full audit trail
  • Real-time Policy Enforcement: Every token request checked against current policies
  • Automatic Token Refresh: Tokens refreshed transparently by the backend
  • Actor Tracking: First-class support for AI agent and MCP server observability

Installation

pip install alter-sdk

Quick Start

import asyncio
from alter_sdk import AlterVault, Provider, HttpMethod

async def main():
    vault = AlterVault(api_key="alter_key_...")

    # Make API request - token injected automatically, never exposed
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
        query_params={"maxResults": "10"},
    )
    events = response.json()
    print(events)

    await vault.close()

asyncio.run(main())

Usage

Simple GET Request

response = await vault.request(
    Provider.GOOGLE,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    user={"user_id": "alice"},
)

POST with JSON Body

response = await vault.request(
    Provider.SALESFORCE,
    HttpMethod.POST,
    "https://api.example.com/v1/items",
    user={"user_id": "alice"},
    json={"name": "New Item", "price": 99.99},
    reason="Creating new item",
)

URL Path Templating

response = await vault.request(
    Provider.SALESFORCE,
    HttpMethod.PUT,
    "https://api.example.com/v1/items/{item_id}",
    user={"user_id": "alice"},
    path_params={"item_id": "123"},
    json={"price": 89.99},
)

Query Parameters and Extra Headers

response = await vault.request(
    "notion",
    HttpMethod.POST,
    "https://api.notion.com/v1/databases/{db_id}/query",
    user={"user_id": "alice"},
    path_params={"db_id": "abc123"},
    extra_headers={"Notion-Version": "2022-06-28"},
    json={"page_size": 10},
)

Context Manager

async with AlterVault(api_key="alter_key_...") as vault:
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
    )
# Automatically closed

Note: After close() is called, subsequent request() calls raise AlterSDKError. close() is idempotent — calling it multiple times is safe.

Connection Management

List Connections

Retrieve OAuth connections for your app, optionally filtered by provider:

from alter_sdk import AlterVault

async with AlterVault(api_key="alter_key_...") as vault:
    # List all connections
    result = await vault.list_connections()
    for conn in result.connections:
        print(f"{conn.provider_id}: {conn.account_display_name} ({conn.status})")

    # Filter by provider with pagination
    result = await vault.list_connections(provider_id="google", limit=10, offset=0)
    print(f"Total: {result.total}, Has more: {result.has_more}")
Parameter Type Default Description
provider_id str | None None Filter by provider (e.g., "google")
limit int 100 Max connections to return
offset int 0 Pagination offset

Returns ConnectionListResult with: connections (list[ConnectionInfo]), total, limit, offset, has_more.

Create Connect Session

Generate a session URL for end-users to authenticate with OAuth providers:

session = await vault.create_connect_session(
    end_user={"id": "alice"},
    allowed_providers=["google", "github"],
    return_url="https://myapp.com/callback",
)
print(f"Connect URL: {session.connect_url}")
print(f"Expires in: {session.expires_in}s")
Parameter Type Default Description
end_user dict required Must contain "id" key
attributes dict | None None Connection attributes
allowed_providers list[str] | None None Restrict to specific providers
return_url str | None None Redirect URL after OAuth flow

Returns ConnectSession with: session_token, connect_url, expires_in, expires_at.

AI Agent Actor Tracking

vault = AlterVault(
    api_key="alter_key_...",
    actor_type="ai_agent",
    actor_identifier="email-assistant-v2",
    actor_name="Email Assistant",
    actor_version="2.0.0",
    framework="langgraph",
)

response = await vault.request(
    Provider.GOOGLE,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    user={"user_id": "alice"},
    run_id="550e8400-e29b-41d4-a716-446655440000",
    thread_id="thread-xyz",
    tool_call_id="call_abc_123",
)

Multi-Agent Deployments

Each agent must create its own AlterVault instance with a unique actor identity. Do not share a single instance across agents.

# Each agent gets its own vault instance
email_agent = AlterVault(
    api_key="alter_key_...",
    actor_type="ai_agent",
    actor_identifier="email-assistant-v2",
    actor_name="Email Assistant",
)

calendar_agent = AlterVault(
    api_key="alter_key_...",
    actor_type="ai_agent",
    actor_identifier="calendar-agent-v1",
    actor_name="Calendar Agent",
)

# Audit logs and policies are tracked per agent
user = {"user_id": "alice"}
await email_agent.request(
    Provider.GOOGLE, HttpMethod.GET,
    "https://gmail.googleapis.com/gmail/v1/users/me/messages",
    user=user,
)
await calendar_agent.request(
    Provider.GOOGLE, HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    user=user,
)

# Clean up each instance
await email_agent.close()
await calendar_agent.close()

Configuration

vault = AlterVault(
    api_key="alter_key_...",              # Required: Alter Vault API key
    base_url="https://api.alter.com",     # Optional: Custom API URL
    timeout=30.0,                         # Optional: HTTP timeout in seconds
    # Actor tracking (optional)
    actor_type="ai_agent",               # "ai_agent" or "mcp_server"
    actor_identifier="my-agent",         # Unique identifier
    actor_name="My Agent",               # Human-readable name
    actor_version="1.0.0",               # Version string
    framework="langgraph",               # AI framework
    client_type="cursor",                # MCP client type
)

Error Handling

from alter_sdk import AlterVault, Provider, HttpMethod
from alter_sdk.exceptions import (
    AlterSDKError,              # Base exception for all SDK errors (including validation: api_key, actor_type, URL scheme, path_params)
    PolicyViolationError,       # Policy denied access (403)
    ConnectionNotFoundError,    # No OAuth connection found (404)
    TokenExpiredError,          # Token refresh failed (400/502)
    TokenRetrievalError,        # Other backend errors
    NetworkError,               # Backend or provider unreachable
    TimeoutError,               # Request timed out (subclass of NetworkError)
    ProviderAPIError,           # Provider API returned error (4xx/5xx)
)

try:
    response = await vault.request(
        Provider.GOOGLE,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        user={"user_id": "alice"},
    )
except PolicyViolationError as e:
    print(f"Policy denied: {e.message}")
    print(f"Policy error: {e.policy_error}")  # Detailed policy failure reason
except ConnectionNotFoundError:
    print("No OAuth connection - user needs to authenticate")
except TokenExpiredError as e:
    print(f"Token expired for connection: {e.connection_id}")
except TimeoutError as e:
    print(f"Request timed out — safe to retry: {e.message}")
except NetworkError as e:
    print(f"Network issue: {e.message}")
except ProviderAPIError as e:
    print(f"Provider error {e.status_code}: {e.response_body}")

Supported Providers

from alter_sdk import Provider

Provider.GOOGLE       # "google"
Provider.GITHUB       # "github"
Provider.SLACK        # "slack"
Provider.MICROSOFT    # "microsoft"
Provider.SALESFORCE   # "salesforce"
Provider.SENTRY       # "sentry"

# Strings also work for any provider
await vault.request("notion", HttpMethod.GET, url, user=user)

Requirements

  • Python 3.11+
  • httpx[http2]
  • pydantic

License

MIT 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

alter_sdk-0.2.2.tar.gz (20.0 kB view details)

Uploaded Source

Built Distribution

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

alter_sdk-0.2.2-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file alter_sdk-0.2.2.tar.gz.

File metadata

  • Download URL: alter_sdk-0.2.2.tar.gz
  • Upload date:
  • Size: 20.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for alter_sdk-0.2.2.tar.gz
Algorithm Hash digest
SHA256 0784bc92e461b778101bb3440158c314b4c12ba7ef55cbee6586ba499b1f267c
MD5 8d3809e034b98d3025160f30853d7ee8
BLAKE2b-256 af8ee269de6107e6bfd622c67dc959ffaa2e060586895a3f6525e7ed5b402def

See more details on using hashes here.

Provenance

The following attestation bundles were made for alter_sdk-0.2.2.tar.gz:

Publisher: python-sdk-release.yml on AlterAIDev/Alter-Vault

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

File details

Details for the file alter_sdk-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: alter_sdk-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 20.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for alter_sdk-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 45b524d50f703e22405115f711ed34c62e235b87f71fe60e678451e4b18de92f
MD5 b4951afa95410d216a63879563d06947
BLAKE2b-256 dec5e3cf3201ade45eea346b233e22ee000d9324f22b4989aef15f2c0b319f4e

See more details on using hashes here.

Provenance

The following attestation bundles were made for alter_sdk-0.2.2-py3-none-any.whl:

Publisher: python-sdk-release.yml on AlterAIDev/Alter-Vault

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