vault.request() acts as the authenticated HTTP client. Instead of calling fetch directly and managing auth headers, pass a grantId and URL — the SDK makes the request with the correct credential injected automatically. Application code never sees tokens or secrets.
const response = await vault.request( method, // HttpMethod enum or string (e.g., HttpMethod.GET, "GET") url, // Full URL of the provider API endpoint { grantId: "...", // Grant ID (UUID string) from Alter Connect json: {...}, // Optional: JSON request body queryParams: {}, // Optional: URL query parameters pathParams: {}, // Optional: URL path template substitutions extraHeaders: {}, // Optional: Additional headers (Authorization is auto-injected) reason: "...", // Optional: Reason for audit trail context: {}, // Optional: Per-request identity context for audit });
For APIs where the credentials are already available (API keys, service tokens), use Managed Secrets. The grantId comes from the Developer Portal (not from an end user), and usage is identical to OAuth via vault.request():
const vault = new AlterVault({ apiKey: "alter_key_...", caller: "my-agent",});try { // Call your internal API — secret injected automatically const response = await vault.request( HttpMethod.GET, "https://api.internal.com/v1/loyalty/points", { grantId: "MANAGED_SECRET_GRANT_ID", // from Developer Portal (not from a user) queryParams: { user_id: "alice" }, reason: "Checking loyalty points for rewards calculation", } ); const data = await response.json();} finally { await vault.close();}
OAuth vs Managed Secrets: For OAuth, the grantId comes from an end user completing Alter Connect (per-user). For Managed Secrets, the grantId comes from the Developer Portal when a credential is stored (per-service, shared across the backend). The SDK auto-detects the credential type and injects it as the correct header.
Policy enforcement, audit logging, and error handling all work identically for both OAuth and managed secrets. See the Managed Secrets guide for setup details.
The TypeScript SDK currently exposes the core credential-access surface only. Developer applications populate context manually for each request — framework-specific integration packages (FastMCP, LangChain) are available in the Python SDK via alter-sdk[mcp] / alter-sdk[langchain] extras and are planned for the TypeScript SDK in a future release.
Each agent must create its own AlterVault instance with a unique caller identity. Do not share a single instance across agents — audit logs, policies, and caller registration are tied to each instance.
// Each agent gets its own vault instanceconst emailAgent = new AlterVault({ apiKey: "alter_key_...", caller: "email-assistant-v2",});const calendarAgent = new AlterVault({ apiKey: "alter_key_...", caller: "calendar-agent-v1",});// Audit logs and policies are tracked per callerawait emailAgent.request( HttpMethod.GET, "https://gmail.googleapis.com/gmail/v1/users/me/messages", { grantId: gmailGrantId }, // from Alter Connect);await calendarAgent.request( HttpMethod.GET, "https://www.googleapis.com/calendar/v3/calendars/primary/events", { grantId: calendarGrantId }, // from Alter Connect);// Clean up each instanceawait emailAgent.close();await calendarAgent.close();
All agents can use the same API key — the caller identity (set at initialization) is what differentiates them in audit logs and policy enforcement.
Unique identifier for this SDK instance (optional)
"email-assistant-v2"
Per-request (set on each vault.request() call):
Parameter
Type
Description
Example
context
Record<string, string>
Per-request identity and execution context (audit log JSONB)
{ tool: "read_calendar", agent: "cursor" }
The context object is validated by the SDK before being sent: keys and values must be strings, the object must have at most 20 keys, no key longer than 64 chars, no value longer than 512 chars, and the JSON-encoded payload must fit in 4 KB. Violations throw AlterValueError so a malformed context never silently disappears from the audit trail.
Scoping grant operations to the authenticated user
In user-facing applications — where many end users share a single AlterVault instance — configure userTokenGetter so every grant operation is tied to the caller’s identity. The SDK forwards the end user’s IDP JWT and the backend resolves each call against that user’s own grants.
const vault = new AlterVault({ apiKey: "alter_key_...", userTokenGetter: () => getCurrentUserJwt(),});// listGrants() returns the caller's grants.const result = await vault.listGrants();// revokeGrant() targets a grant the caller owns.// Passing a grantId that does not belong to the caller throws// GrantNotFoundError.await vault.revokeGrant(grantId);// createConnectSession() produces a Connect URL tied to the caller's identity.const session = await vault.createConnectSession({ allowedProviders: ["google"],});// vault.request({ grantId, ... }) retrieves a token only when the grant// belongs to the caller. (Or use { provider: "..." } for identity resolution.)await vault.request(HttpMethod.GET, url, { grantId });
If userTokenGetter is configured but cannot produce a token (e.g. the end user’s session has expired), the SDK throws AlterSDKError. Surface this as a 401 to the end user so they can re-authenticate.Omit userTokenGetter for headless tools (scripts, cron jobs, backend migrations) that operate on grants the developer manages directly. The same methods then run under the application’s own credentials.
For CLI tools, scripts, and server-side applications, use connect() to open the browser, wait for the user to complete OAuth, and get the result back:
const results = await vault.connect({ providers: ["google"], timeout: 300_000, // max wait in milliseconds (default: 300000 = 5 min) pollInterval: 2000, // poll cadence in milliseconds openBrowser: true, // set false to print URL instead});for (const result of results) { console.log(`Connected: ${result.grantId} (${result.providerId})`);}// Now use the grantId with vault.request()const response = await vault.request( HttpMethod.GET, "https://www.googleapis.com/calendar/v3/calendars/primary/events", { grantId: results[0].grantId },);
Returns ConnectResult[] — one per connected provider. Each has grantId, providerId, accountIdentifier, scopes, and optionally grantPolicy (with expiresAt if a TTL was set).Throws ConnectTimeoutError if the user doesn’t complete in time, ConnectFlowError if denied.
Use grantPolicy to control how long a grant stays active. When set, the Connect UI shows an expiry picker and the grant automatically expires after the chosen duration.
const results = await vault.connect({ providers: ["google"], grantPolicy: { maxTtlSeconds: 604800, // longest duration the user can pick defaultTtlSeconds: 86400, // pre-selected in the dropdown },});
The Connect UI offers these expiry options: 1 hour, 1 day, 7 days, 30 days, 90 days. Setting maxTtlSeconds hides any option that exceeds it.
Parameter
Description
maxTtlSeconds
Maximum TTL the end user can select. Options above this are hidden from the picker.
defaultTtlSeconds
Pre-selected option in the dropdown. Falls back to 1 day if not set.
The expiry picker only appears when grantPolicy is passed. Without it, grants have no automatic expiry. After a grant expires, token retrieval throws GrantExpiredError and the user must re-authorize via Alter Connect.
The same parameter works with createConnectSession():
const session = await vault.createConnectSession({ allowedProviders: ["google"], grantPolicy: { maxTtlSeconds: 2592000, // max 30 days defaultTtlSeconds: 604800, // default 7 days },});
import { AlterVault, HttpMethod } from "@alter-ai/alter-sdk";import { ReAuthRequiredError, PolicyViolationError, GrantNotFoundError, ScopeReauthRequiredError, NetworkError, ProviderAPIError,} from "@alter-ai/alter-sdk";async function safeApiCall(grantId: string) { const vault = new AlterVault({ apiKey: "alter_key_...", caller: "my-agent", }); try { const response = await vault.request( HttpMethod.GET, "https://www.googleapis.com/calendar/v3/calendars/primary/events", { grantId }, ); return await response.json(); } catch (e) { if (e instanceof ReAuthRequiredError) { // Catches GrantExpiredError, CredentialRevokedError, GrantRevokedError, GrantDeletedError console.log("User must re-authorize via Alter Connect"); } else if (e instanceof PolicyViolationError) { console.log(`Access denied by policy: ${e.message} (rule: ${e.policyError})`); } else if (e instanceof GrantNotFoundError) { console.log("User hasn't connected Google yet"); } else if (e instanceof NetworkError) { // TimeoutError is a subclass, so this catches both console.log(`Network issue: ${e.message}`); } else if (e instanceof ScopeReauthRequiredError) { console.log(`Scope mismatch on ${e.grantId}: re-auth needed`); // Create a new Connect session so the user can grant updated scopes } else if (e instanceof ProviderAPIError) { console.log(`Provider error ${e.statusCode}: ${e.responseBody}`); } else { throw e; } } finally { await vault.close(); }}