Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.alterauth.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide covers integrating Alter Vault into an application. By the end, the setup will support:
  • Connect OAuth accounts and make authenticated API calls
  • Use the Python SDK with automatic token injection
  • Never handle OAuth tokens directly

Two Types of Credentials

Alter Vault manages two types of credentials. Both use the same vault.request() method -the only difference is where the grant_id comes from.
OAuth ConnectionsManaged Secrets
WhatThird-party services (Google, Slack, GitHub)Internal APIs (internal services, partner APIs)
Who provides credentialsEnd user authorizes via OAuth flowDeveloper stores via Developer Portal
Where grant_id comes fromReturned in onSuccess callback when user completes Alter ConnectReturned by Developer Portal when you store a secret
SetupAlter Connect UI (frontend) or vault.connect() (headless)Developer Portal → Managed Secrets → Store Secret
Token refreshAutomatic (OAuth refresh flow)Manual (re-store when rotated)
The key distinction: For OAuth, the end user triggers the grant by logging into their account (Google, Slack, etc.), and the grant_id is returned. For Managed Secrets, you (the developer) store the credential in the portal and get the grant_id back. Once you have a grant_id, usage is identical: vault.request(method, url, grant_id=grant_id).

Two Integration Paths

PathBest forWhat you need
Quick path (below)CLI tools, scripts, Jupyter notebooks, prototypingPython only
Full web app (Step 4+)Production web apps with a frontendPython + JavaScript

Prerequisites

Before you begin, you’ll need:
  • An Alter Vault account (sign up free)
  • An OAuth app with the chosen provider (Google, GitHub, etc.)
  • Python 3.11+

Quick Path: Connect from Python

The fastest way to get started. Everything happens in Python — the SDK opens a browser for the user to authorize, then returns the grant.
import asyncio
from alter_sdk import AlterVault, HttpMethod

async def main():
    vault = AlterVault(
        api_key="alter_key_...",
        caller="my-agent",
    )

    # Open browser → user authorizes → SDK returns connections
    results = await vault.connect(
        providers=["google"],
    )
    print(f"Connected: {results[0].grant_id} ({results[0].provider_id})")

    # Make API calls with the grant
    response = await vault.request(
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        grant_id=results[0].grant_id,
        query_params={"maxResults": "10"},
    )
    events = response.json()
    for event in events.get("items", []):
        print(f"Event: {event['summary']}")

    await vault.close()

asyncio.run(main())
That’s it. No frontend code, no session tokens, no callbacks. The SDK handles the entire OAuth flow.
connect() opens the user’s default browser. Set open_browser=False to print the URL instead (useful for remote servers).
Skip to Step 6 for more usage examples, or continue below for the full web app integration.

Full Web App Integration

For production web apps, use the session-based flow. The backend creates a session and gets back a connect_url - a hosted page where the user completes OAuth.

Choosing an OAuth Trigger Method

MethodWhen to useHow it works
Headless (vault.connect())CLI tools, scripts, Jupyter notebooks, backend agents. No frontend at all.One SDK call does everything — creates a session, opens the user’s default browser, and polls until OAuth completes. Everything happens in Python/TypeScript.
RedirectServer-rendered apps (Django, Rails, etc.), email links, or any app that can redirect a browser. No JavaScript SDK needed.The backend calls create_connect_session(), gets a connect_url, and redirects the user to it. After OAuth, the user is redirected back to the return_url with the results.
Popup (@alter-ai/connect SDK)Single-page apps (React, Vue, etc.) where you want a popup overlay with real-time onSuccess/onError callbacks without leaving the page.The backend calls create_connect_session() and passes the session_token to the frontend SDK, which opens the hosted page in a popup.
The frontend JavaScript SDK (@alter-ai/connect) is optional. The connect_url returned by create_connect_session() is a standard HTTPS URL that works in any browser. The frontend SDK just wraps it in a popup with callbacks for a smoother SPA experience.

Key Components

  1. Python / TypeScript SDK - Backend SDK that creates sessions and makes authenticated API calls via vault.request()
  2. Alter Connect UI - Hosted page (at the connect_url) that handles provider selection and the entire OAuth flow
  3. Connect SDK (@alter-ai/connect) - Optional frontend SDK that opens the hosted page in a popup with callbacks

Step 1: Create an Application

1

Sign in to the Developer Portal

Go to portal.alterauth.com and sign in
2

Create an Application

Click “New Application” and give it a name
3

Get the Credentials

Copy the API Key (starts with alter_key_) - this will be needed soon
4

Configure OAuth Providers

Add the OAuth provider credentials (Google, Slack, GitHub, etc.)

Step 2: Configure the OAuth Provider

Let’s set up Google as an example (other providers work similarly):
1

Go to Google Cloud Console

2

Create OAuth Credentials

  1. Go to “APIs & Services” → “Credentials”
  2. Click “Create Credentials” → “OAuth client ID”
  3. Choose “Web application”
  4. Add the Redirect URI from the Developer Portal
3

Add to Alter Vault

In the Alter Vault dashboard:
  1. Go to “OAuth Providers” → “Add Provider”
  2. Select “Google”
  3. Enter the Client ID and Client Secret
  4. Save

Step 3: Install the SDK

pip install alter-sdk
If you’re using the popup flow (SPAs), also install the optional frontend SDK: npm install @alter-ai/connect. For the redirect flow or headless flow, only the backend SDK is needed.

Step 4: Create a Session

The backend calls create_connect_session() which returns:
  • connect_url - a hosted page where the user completes OAuth (use this for the redirect flow)
  • session_token - a short-lived token to pass to the frontend SDK (use this for the popup flow)
import asyncio
from flask import Flask, request, jsonify, redirect
from alter_sdk import AlterVault

app = Flask(__name__)

vault = AlterVault(
    api_key="alter_key_...",
    caller="my-flask-app",
)

@app.route('/api/connect/session', methods=['POST'])
def create_oauth_session():
    user = request.user  # Your auth

    async def _create():
        session = await vault.create_connect_session(
            allowed_providers=["google", "slack", "github"],
        )
        return session

    session = asyncio.run(_create())

    # Return both - your frontend decides which to use
    return jsonify({
        "session_token": session.session_token,  # For popup flow
        "connect_url": session.connect_url,       # For redirect flow
    })

Step 5: Trigger the OAuth Flow

Choose the approach that fits the application:

Option A: Redirect Flow (no frontend SDK needed)

Redirect the user’s browser to the connect_url. After OAuth completes, the user is redirected back to the application. This works with any backend framework - no JavaScript SDK required.
@app.route('/connect/<provider>')
def connect_provider(provider):
    user = request.user  # Your auth

    async def _create():
        session = await vault.create_connect_session(
            allowed_providers=[provider],
            return_url="https://myapp.com/connect/callback",
        )
        return session

    session = asyncio.run(_create())
    return redirect(session.connect_url)  # User goes to hosted OAuth page

@app.route('/connect/callback')
def connect_callback():
    # User returns here after completing OAuth
    # The grant_id is available via vault.list_grants()
    return "Connected! You can close this tab."

Option B: Popup Flow (frontend SDK)

Use the @alter-ai/connect SDK to open the OAuth page in a popup with real-time callbacks. The user stays on the current page.
import AlterConnect from '@alter-ai/connect';

function IntegrationsPage() {
    const alterConnect = AlterConnect.create();

    const handleConnect = async (provider) => {
        // Call YOUR backend to create a session (see Step 4)
        const { session_token } = await fetch('/api/connect/session', {
            method: 'POST',
            headers: { 'Authorization': `Bearer ${authToken}` }
        }).then(r => r.json());

        // Open Alter Connect UI -user logs into their account and authorizes
        await alterConnect.open({
            token: session_token,
            onSuccess: (connections) => {
                // User completed OAuth! Store each grant_id in your database.
                for (const conn of connections) {
                    console.log('Connected!', conn.grant_id, conn.provider);
                }
                loadIntegrations();
            },
            onError: (error) => {
                console.error('Connection failed', error);
            }
        });
    };

    return (
        <div>
            <button onClick={() => handleConnect('google')}>
                Connect Google
            </button>
            <button onClick={() => handleConnect('slack')}>
                Connect Slack
            </button>
        </div>
    );
}

Step 6: Use the Python SDK

Now use the SDK to make API calls with automatic token injection:

Basic Usage

import asyncio
from alter_sdk import AlterVault, HttpMethod

async def fetch_calendar_events(user_id: str):
    vault = AlterVault(
        api_key="alter_key_...",
        caller="my-agent",
    )

    # Your app stores grant_id when the user completes Alter Connect (Step 5)
    grant_id = db.get_grant(user_id=user_id, provider="google")

    try:
        # Make API request -token is injected automatically
        response = await vault.request(
            HttpMethod.GET,
            "https://www.googleapis.com/calendar/v3/calendars/primary/events",
            grant_id=grant_id,
            query_params={
                "maxResults": "10",
                "singleEvents": "true",
                "orderBy": "startTime",
            },
            reason="Fetching calendar events",
        )

        events = response.json()
        for event in events.get("items", []):
            start = event["start"].get("dateTime", event["start"].get("date"))
            print(f"{start}: {event.get('summary', 'No title')}")

        return events

    finally:
        await vault.close()

asyncio.run(fetch_calendar_events("alice"))

Using Context Managers

async with AlterVault(
    api_key="alter_key_...",
    caller="my-agent",
) as vault:
    response = await vault.request(
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        grant_id="GRANT_ID",  # from Alter Connect
    )
    events = response.json()
# Clients automatically closed

POST Requests and URL Templating

async with AlterVault(
    api_key="alter_key_...",
    caller="my-agent",
) as vault:
    # POST with JSON body
    response = await vault.request(
        HttpMethod.POST,
        "https://slack.com/api/chat.postMessage",
        grant_id=slack_grant_id,  # from Alter Connect
        json={"channel": "#general", "text": "Hello from Alter!"},
    )

    # URL path templating with automatic URL encoding
    response = await vault.request(
        HttpMethod.GET,
        "https://api.example.com/v1/accounts/{account_id}/contacts",
        grant_id=sentry_grant_id,  # from Alter Connect
        path_params={"account_id": "acc-123"},
        query_params={"limit": "50"},
    )

Error Handling

from alter_sdk.exceptions import (
    ReAuthRequiredError,
    PolicyViolationError,
    GrantNotFoundError,
    NetworkError,
    ScopeReauthRequiredError,
    ProviderAPIError,
)

try:
    response = await vault.request(
        HttpMethod.GET,
        "https://www.googleapis.com/calendar/v3/calendars/primary/events",
        grant_id=grant_id,
    )
except ReAuthRequiredError:
    print("User must re-authorize - redirect to Alter Connect")
except PolicyViolationError as e:
    print(f"Access denied by policy: {e.message} (rule: {e.policy_error})")
except GrantNotFoundError:
    print("User hasn't connected Google yet - redirect to Alter Connect")
except NetworkError as e:
    print(f"Network issue: {e.message}")
except ScopeReauthRequiredError as e:
    print(f"Scopes outdated for {e.provider_id} - user must re-authorize (connection: {e.grant_id})")
except ProviderAPIError as e:
    print(f"Google API error {e.status_code}: {e.response_body}")

AI Agent Integration

If you’re building AI agents, add actor tracking for full observability:
vault = AlterVault(
    api_key="alter_key_...",
    caller="email-assistant-v2",
)

# Caller identity is sent automatically on every request
# Per-request context adds execution detail for audit logs
response = await vault.request(
    HttpMethod.GET,
    "https://gmail.googleapis.com/gmail/v1/users/me/messages",
    grant_id="GRANT_ID",  # from Alter Connect
    context={"tool": "read_emails", "agent": "email-assistant-v2"},
    reason="Reading emails for daily digest",
)
See the Python SDK Overview for the complete caller tracking guide.

Complete Example: Flask + Google Calendar

Here’s a full example showing a Flask API that fetches calendar events:
import asyncio
from flask import Flask, jsonify, request
from alter_sdk import AlterVault, HttpMethod
from alter_sdk.exceptions import GrantNotFoundError

app = Flask(__name__)
vault = AlterVault(
    api_key="alter_key_...",
    caller="my-flask-app",
)

@app.route('/api/users/<user_id>/events')
def get_user_events(user_id):
    """Fetch user's calendar events using Alter SDK."""
    # Your app stores grant_id when the user completes Alter Connect (Step 5)
    grant_id = db.get_grant(user_id=user_id, provider="google")

    async def fetch_events():
        try:
            response = await vault.request(
                HttpMethod.GET,
                "https://www.googleapis.com/calendar/v3/calendars/primary/events",
                grant_id=grant_id,
                query_params={
                    "maxResults": "10",
                    "singleEvents": "true",
                    "orderBy": "startTime",
                },
                reason="User viewing calendar events",
            )
            return response.json()
        except GrantNotFoundError:
            return None

    result = asyncio.run(fetch_events())

    if result is None:
        return jsonify({"error": "User hasn't connected Google account"}), 404

    return jsonify(result)

if __name__ == '__main__':
    app.run(debug=True)

Alternative: Using Managed Secrets

For internal APIs (internal services, partner APIs, SaaS platforms with API keys), the OAuth flow is not needed. Use Managed Secrets instead.
How is this different from OAuth? With OAuth, the grant_id comes from the end user completing an OAuth login. With Managed Secrets, the grant_id comes from you (the developer) storing a credential in the Developer Portal. No end-user action is needed.

Step 1: Store the Secret in the Developer Portal

  1. Go to the Developer Portal → the application → Managed Secrets
  2. Click Add Provider (e.g., “Loyalty API”)
  3. Choose credential type (Bearer Token, API Key, Basic Auth, or AWS SigV4)
  4. Click Store Secret and enter the credential value
  5. Copy the grant_id returned -this is what gets used in application code

Step 2: Use vault.request()

Use the exact same vault.request() method -just pass the managed secret’s grant_id:
from alter_sdk import AlterVault, HttpMethod

async with AlterVault(
    api_key="alter_key_...",
    caller="my-agent",
) as vault:
    # Call your internal API -secret injected automatically
    response = await vault.request(
        HttpMethod.GET,
        "https://api.internal.com/v1/loyalty/points",
        grant_id="MANAGED_SECRET_GRANT_ID",  # from Developer Portal
        query_params={"user_id": "alice"},
        reason="Checking loyalty points",
    )
    points = response.json()
Same security guarantees: Managed secrets get the same zero-exposure encryption, policy enforcement, and audit logging as OAuth tokens. See the Managed Secrets guide for details.

Key Concepts

Zero Token Exposure

vault.request() is the HTTP client -it replaces direct fetch or httpx calls. Instead of constructing auth headers manually, pass a grant_id and the target URL. The SDK:
  • Makes the HTTP request on behalf of the caller
  • Retrieves the credential internally and injects it as the correct auth header
  • Returns the API response directly to you
  • Refreshes OAuth tokens transparently before they expire
Application code never sees, stores, or handles any tokens or secrets -eliminating the risk of accidental leakage through logs, error messages, or insecure storage.

Where Does grant_id Come From?

Every credential in Alter Vault has a unique grant_id (UUID). Where it originates depends on the credential type:
  • OAuth grants: The grant_id is returned in the onSuccess callback when the end user completes the OAuth flow via Alter Connect. Store this in the application database, mapped to users.
  • Managed secrets: The grant_id is returned by the Developer Portal when a credential is stored. Hard-code it in the config or environment variables.
# OAuth: grant_id stored in your DB after user completed Alter Connect
grant_id = db.get_grant(user_id=user_id, provider="google")
await vault.request(HttpMethod.GET, url, grant_id=grant_id)

# Managed secret: grant_id from Developer Portal, stored in your config
await vault.request(HttpMethod.GET, url, grant_id=os.environ["LOYALTY_API_GRANT_ID"])

Multiple Connections (Multi-Account)

A user can connect multiple accounts to the same provider (e.g., personal Gmail + work Gmail). Each connection gets its own grant_id. Store the account identifier alongside the grant_id when the user completes Alter Connect:
// In your Alter Connect onSuccess callback (Step 5)
onSuccess: async (connections) => {
    for (const conn of connections) {
        await saveToDatabase({
            user_id: currentUser.id,
            grant_id: conn.grant_id,
            provider_id: conn.provider,
            account: conn.account_identifier  // e.g., "[email protected]"
        });
    }
}
Look up connections when making API calls:
# Simple case: one grant per user per provider
grant_id = db.get_grant(user_id=user_id, provider="google")

# Multi-account: user picks which account to use
grants = db.get_grants(user_id=user_id, provider="google")
# Returns: [("aaa-111", "[email protected]"), ("bbb-222", "[email protected]")]
List connections via the SDK to see what a user has connected:
result = await vault.list_grants(provider_id="google")
for grant in result.grants:
    print(f"{grant.grant_id}: {grant.account_display_name}")

Provider Support

The SDK works with any OAuth provider configured in the dashboard. Pass the grant_id as a keyword argument:
# Use the grant_id from Alter Connect
await vault.request(HttpMethod.GET, url, grant_id=google_grant_id)   # from Alter Connect
await vault.request(HttpMethod.GET, url, grant_id=github_grant_id)   # from Alter Connect
await vault.request(HttpMethod.POST, url, grant_id=slack_grant_id)   # from Alter Connect

Best Practices

Security

  • Keep API keys server-side only
  • Use session tokens (short-lived) for frontend
  • Set IP restrictions in production

Grant Management

  • Store the grant_id from Alter Connect in the application database
  • Map grant IDs to users for easy lookup
  • Use listGrants() to retrieve grant IDs programmatically

Error Handling

  • Always handle PolicyViolationError and GrantNotFoundError explicitly
  • Don’t catch all exceptions with a bare except Exception
  • Use retry logic for NetworkError (with exponential backoff)

Troubleshooting

Grant Not Found

If you get GrantNotFoundError, ensure:
  • User has completed OAuth flow via Alter Connect
  • The grant_id is correct and belongs to the application
  • Connection hasn’t been revoked

Token Expired

The SDK automatically refreshes tokens. If refresh fails:
  • User may need to re-authenticate
  • Check if refresh token has expired
  • Verify OAuth app credentials are still valid

Policy Violation

If you get PolicyViolationError:
  • Check the error message for the specific rule that failed (time-based access or IP allowlist)
  • Review the policy configuration in App Details -> Policies in the Developer Portal
  • If using IP allowlist, confirm the server’s IP is included
  • If using time restrictions, check if you’re within business hours / weekdays

Provider Not Configured

If you get BackendError with “not configured”:
  • Add the provider in the Alter Vault dashboard
  • Enter the OAuth Client ID and Client Secret

What’s Next?

Python SDK

Deep dive into the Python SDK with actor tracking

Alter Connect

Customize the OAuth UI

Architecture

Understand the security architecture

Audit Logs

Compliance and observability

Portal Guide

Configure providers and policies

Need Help?