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 - Credential management for agents 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
  • API Key and Custom Credential Support: Handles OAuth tokens, API keys, and custom credential formats automatically
  • HMAC Request Signing: All SDK-to-backend requests are signed with a derived HMAC-SHA256 key for integrity, authenticity, and replay protection
  • 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, ActorType, Provider, HttpMethod

async def main():
    vault = AlterVault(
        api_key="alter_key_...",
        actor_type=ActorType.AI_AGENT,
        actor_identifier="my-agent",
    )

    # Make API request — token injected automatically, never exposed
    response = await vault.request(
        "CONNECTION_ID",  # from Alter Connect (see below)
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        query_params={"maxResults": "10"},
    )
    events = response.json()
    print(events)

    await vault.close()

asyncio.run(main())

Where does connection_id come from?

OAuth connections:

  1. User completes OAuth via Alter Connect (frontend widget)
  2. The onSuccess callback returns a connection_id (UUID)
  3. You save it in your database, mapped to your user
  4. You pass it to vault.request() when making API calls

Managed secrets (API keys, service tokens):

  1. Store credentials in the Developer Portal under Managed Secrets
  2. Copy the connection_id returned
  3. Use the same vault.request() — credentials are injected automatically
# You can also discover connection_ids programmatically:
result = await vault.list_connections(provider_id="google")
for conn in result.connections:
    print(f"{conn.id}: {conn.account_display_name}")

Usage

The request() method returns the raw httpx.Response. The token is injected automatically and never exposed.

Simple GET Request

response = await vault.request(
    connection_id,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
)

POST with JSON Body

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

URL Path Templating

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

Query Parameters and Extra Headers

response = await vault.request(
    connection_id,
    HttpMethod.POST,
    "https://api.notion.com/v1/databases/{db_id}/query",
    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_...",
    actor_type=ActorType.BACKEND_SERVICE,
    actor_identifier="my-service",
) as vault:
    response = await vault.request(
        connection_id,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    )
# Automatically closed

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

Using Managed Secrets

For your own APIs with API keys or service tokens (no OAuth flow needed):

async with AlterVault(
    api_key="alter_key_...",
    actor_type=ActorType.BACKEND_SERVICE,
    actor_identifier="my-service",
) as vault:
    response = await vault.request(
        "MANAGED_SECRET_CONNECTION_ID",  # from Developer Portal
        HttpMethod.GET,
        "https://api.internal.com/v1/data",
    )

The credential is injected automatically as the configured header type (Bearer, API Key, Basic Auth).

Connection Management

List Connections

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

from alter_sdk import AlterVault, ActorType

async with AlterVault(
    api_key="alter_key_...",
    actor_type=ActorType.BACKEND_SERVICE,
    actor_identifier="my-service",
) 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
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

from alter_sdk import AlterVault, ActorType, Provider, HttpMethod

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

response = await vault.request(
    connection_id,
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    run_id="550e8400-e29b-41d4-a716-446655440000",  # auto-generated UUID if omitted
    thread_id="thread-xyz",
    tool_call_id="call_abc_123",
)

Note: run_id is auto-generated as a UUID v4 if not provided. All sub-actions within a single request() call share the same run_id for audit log grouping.

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=ActorType.AI_AGENT,
    actor_identifier="email-assistant-v2",
    actor_name="Email Assistant",
)

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

# Audit logs and policies are tracked per agent
await email_agent.request(
    gmail_connection_id,  # from Alter Connect
    HttpMethod.GET,
    "https://gmail.googleapis.com/gmail/v1/users/me/messages",
)
await calendar_agent.request(
    calendar_connection_id,  # from Alter Connect
    HttpMethod.GET,
    "https://www.googleapis.com/calendar/v3/calendars/primary/events",
)

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

Configuration

from alter_sdk import AlterVault, ActorType

vault = AlterVault(
    api_key="alter_key_...",              # Required: Alter Vault API key
    actor_type=ActorType.AI_AGENT,        # Required: ActorType enum
    actor_identifier="my-agent",          # Required: Unique identifier
    timeout=30.0,                         # Optional: HTTP timeout in seconds
    actor_name="My Agent",               # Optional: Human-readable name
    actor_version="1.0.0",               # Optional: Version string
    framework="langgraph",               # Optional: AI framework
    client_type="cursor",                # Optional: 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, actor_identifier, 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(
        connection_id,
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
    )
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.SENTRY       # "sentry"

# Provider enums are used for filtering (e.g., list_connections(provider_id=Provider.GOOGLE))
# request() takes a connection_id string as its first argument
await vault.request(connection_id, HttpMethod.GET, url)

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.3.1.tar.gz (23.2 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.3.1-py3-none-any.whl (22.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: alter_sdk-0.3.1.tar.gz
  • Upload date:
  • Size: 23.2 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.3.1.tar.gz
Algorithm Hash digest
SHA256 3233455bd37c7eae1a022c0324437f7c10c488a92627e97b2af3b7bef71adda1
MD5 070309c4f32a204aba58af25a12fdfb5
BLAKE2b-256 f40ba3fde21f172dacac5ab90f66fa58e93a60aa2c89f7da01c89f33314f07f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for alter_sdk-0.3.1.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.3.1-py3-none-any.whl.

File metadata

  • Download URL: alter_sdk-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 22.1 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.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5191aa3e61ad2288d989d8b0256659dcc4cf51d754ed04c53aca8d397f726a70
MD5 7f82cdf1d321b6447f9cc1a2a38d91e8
BLAKE2b-256 5d8a54f9f914a38b09fcdf15ef29dfb7463188c303a3518afc1c316542c1402e

See more details on using hashes here.

Provenance

The following attestation bundles were made for alter_sdk-0.3.1-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