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.
The TypeScript SDK ships as the @alter-ai/alter-sdk package on npm. Node 20+. Server-side; for browser-side OAuth popups, see the Connect SDK.
npm install @alter-ai/alter-sdk
For end-to-end walkthroughs, see the Quickstart and Guides. This page is API reference only.
Clients
Two classes.
App
The application-side client. Construct with an alter_key_app_… API key.
import { App, CallerType } from "@alter-ai/alter-sdk";
const app = new App({
apiKey: string,
timeout?: number, // ms, default 30000
caller?: string,
callerType?: CallerType, // default SERVICE
userTokenGetter?: () => string | Promise<string>,
logger?: AlterLogger,
});
| Option | Type | Default |
|---|
apiKey | string | — |
timeout | number (ms) | 30000 |
caller | string | — |
callerType | CallerType | "agent" | "service" | SERVICE |
userTokenGetter | () => string | Promise<string> | — |
logger | AlterLogger | — |
Resources are released with await app.close(). The SDK does not auto-close on process exit.
Agent
The workload-side client. Construct with an alter_key_agent_… key (or via app.getAgent(agentId) for the impersonation path).
import { Agent } from "@alter-ai/alter-sdk";
const agent = new Agent({
apiKey: string,
timeout?: number,
caller?: string,
userTokenGetter?: () => string | Promise<string>,
logger?: AlterLogger,
});
Agent exposes a strict subset of App — methods that don’t apply to an agent principal are absent (TypeScript flags the missing methods at compile time).
Making requests
request and proxyRequest are the two runtime modes. They share the same option shape; the difference is where the third-party call happens and what the SDK returns.
| request (retrieve) | proxyRequest (proxy) |
|---|
| Token returned to SDK | Yes — to the SDK process | No — stays on the backend |
| Third-party call made by | The SDK | The Alter backend |
| Return type | Promise<AlterResponse> | Promise<ApprovalResult | PendingApproval> |
| Scope required on the API key | tokens:retrieve | proxy:execute |
| HITL grants | Rejected (hitl_grant_requires_proxy) | Required path |
A non-HITL grant works in either mode. Pick proxy when wire-level audit, strong token isolation, or backend-side policy enforcement matter; pick retrieve for long-lived clients, streaming, or any path where the extra hop hurts. See the runtime modes page for the full tradeoffs.
request(method, url, options?) -> Promise<AlterResponse>
Retrieve-mode call. The SDK fetches a token from the backend, injects it into the outgoing provider request, and makes the call itself.
const response = await app.request(
HttpMethod.GET,
"https://www.googleapis.com/calendar/v3/calendars/primary/events",
{
provider: "google",
queryParams: { maxResults: "10" },
reason: "Sync events on page load",
},
);
AlterResponse extends the standard Response; response.retryInfo exposes refresh-retry metadata when applicable.
Options:
| Option | Description |
|---|
grantId | Explicit grant. |
provider | Resolve from userTokenGetter. |
json | Request JSON body. |
headers | Extra request headers. |
queryParams | Query-string parameters. |
reason | Audit reason. |
context | Application metadata (Record<string, string>). |
userToken | Per-call JWT override. |
account | Disambiguator for ambiguous JWT resolution. |
proxyRequest(method, url, options?) -> Promise<ApprovalResult | PendingApproval>
Proxy-mode call. The backend holds the token, makes the outgoing call, and returns the result — application code and the SDK never observe the token.
Use proxy mode whenever you want the backend to be the credential boundary, regardless of whether HITL is configured. For grants with HITL configured, this is the only path that works: the backend gates the call on the approval decision and only then executes.
Options:
| Option | Description |
|---|
grantId | Explicit grant. |
provider | Resolve from userTokenGetter. |
json | Request JSON body. |
headers | Extra request headers (Authorization is auto-injected on the backend). |
queryParams | Query-string parameters. |
reason | Audit reason — also shown to the approver when HITL is configured. |
context | Application metadata (Record<string, string>). |
userToken | Per-call JWT override. |
account | Disambiguator for ambiguous JWT resolution. |
Returns:
ApprovalResult — the call ran synchronously (no approval required, or approval was pre-granted). Contains the third-party response.
PendingApproval — an approver must act before the call executes. Poll with getApprovalStatus(approvalId) or block with awaitApproval(approvalId, { timeout }). See the HITL guide.
Branch on the return type to handle both cases:
const result = await app.proxyRequest(
HttpMethod.POST,
"https://api.example.com/transfer",
{
grantId,
json: { amount: 1000 },
reason: "Quarterly payout",
},
);
if (result.kind === "pending") {
// surface to the user, wait for approval, then poll
} else {
// ran synchronously; result.response is the provider response
}
boto3Client(grantId, service, region?)
Returns an AWS SDK client signed with credentials from an AWS managed-secret grant.
Grants
listGrants(options?) -> Promise<UnifiedGrantListResult>
Paginated. Each item is either OAuthGrantItem or ManagedSecretGrantItem, discriminated on grantKind.
revokeGrant(grantId, options?) -> Promise<RevokeGrantResult>
App-only.
createManagedSecretGrant(managedSecretId, options) -> Promise<CreateGrantResult>
options.principal is one of UserPrincipal, GroupPrincipal, SystemPrincipal, AgentPrincipal.
revokeDelegation(grantId, agentId?)
App: revokeDelegation(grantId, agentId). Agent: revokeDelegation(grantId).
Connect flow
createConnectSession(options) -> Promise<ConnectSession>
createManagedSecretConnectSession(options) -> Promise<ManagedSecretConnectSession>
connect(options) -> Promise<ConnectResult[]> — headless flow.
Authentication helpers (App only)
authenticate(options?) -> Promise<AuthResult>
verifyUserToken(token) -> Promise<string | null>
Approvals
getApprovalStatus(approvalId) -> Promise<ApprovalStatus>
awaitApproval(approvalId, options?) -> Promise<ApprovalResult>
Managed agents (App only)
app.agents:
create(options) -> Promise<AgentCreateResult> — plaintext key returned once.
list(options?) -> Promise<AgentListResult>
get(agentId) -> Promise<AgentInfo>
getByName(name) -> Promise<AgentInfo | null>
update(agentId, options) -> Promise<AgentInfo>
mintKey(agentId) -> Promise<AgentKeyMintResult>
listKeys(agentId) -> Promise<AgentKeyList>
deprecateKey(agentId, keyId), undeprecateKey(...), revokeKey(agentId, keyId, { force?: boolean })
Scoped key derivation
app.keys / agent.keys:
derive({ scopes, expiresIn, cidrAllowlist?, rateLimitRpm? }) -> Promise<MintedKey>
rotate(keyId) -> Promise<MintedKey>
revoke(keyId) -> Promise<APIKeyInfo>
Scope catalog
app.scopes.getCatalog() -> Promise<ScopeCatalog>
Attenuation
app.withConstraints({ scopes }) -> App — narrow-only sibling instance. See Scopes.
Agent introspection (Agent only)
agent.me() -> Promise<AgentInfo>
- (trace API for nested-call audit scoping lands in a follow-up TS release; today the Python SDK is the recommended path for multi-step agents.)
Models
Public types exported from @alter-ai/alter-sdk:
| Type | Purpose |
|---|
TokenResponse | OAuth token metadata. |
GrantInfo | Single-grant record. |
OAuthGrantItem, ManagedSecretGrantItem, GrantListItem, UnifiedGrantListResult | Discriminated-union grant types. |
ConnectSession, ManagedSecretConnectSession, ConnectResult, AuthResult | Connect / auth results. |
Principal (UserPrincipal, GroupPrincipal, SystemPrincipal, AgentPrincipal) | Managed-secret grant principal. |
GrantPolicy | TTL and policy fields. |
PendingApproval, ApprovalStatus, ApprovalResult | HITL flow. |
AgentInfo, AgentCreateResult, AgentListResult | Managed agents. |
AgentKey, AgentKeyMintResult, AgentKeyList, AgentType, AgentStatus, AgentKeyStatus | Per-agent keys. |
APIKeyInfo, MintedKey | Derived API keys. |
ScopeCatalog, ResourceScopes | Scope catalog. |
RetryInfo, RetryErrorInfo | Retry metadata. |
AlterResponse | The Response subtype returned by request(). |
All wire-shape responses use snake_case JSON; the SDK constructors accept snake_case input and expose camelCase getters.
Errors
See Errors for the full hierarchy. The classes exported from @alter-ai/alter-sdk:
AlterSDKError, AlterValueError, BackendError, ReAuthRequiredError, GrantExpiredError, GrantRevokedError, CredentialRevokedError, GrantDeletedError, GrantNotFoundError, AmbiguousGrantError, PolicyViolationError, InsufficientScopeError, TokenRefreshInProgressError, NoDelegatedGrantError, ConnectFlowError, ConnectDeniedError, ConnectConfigError, ConnectTimeoutError, ProviderAPIError, ScopeReauthRequiredError, NetworkError, TimeoutError, ApprovalError, ApprovalDeniedError, ApprovalExpiredError, ApprovalTimeoutError, ApprovalExecutionFailedError, AgentError, InvalidKeyError, AgentNotFoundError, AgentNameExistsError, MeRequiresAgentKeyError, KeyRevokedError, AgentInactiveError, AgentRevokedError, IdempotencyKeyBodyMismatchError, IdempotencyKeyAgentRevokedError, IdempotencyKeyAgentInactiveError, AgentCannotMintSubagentsError, AgentScopeNarrowingNotSupportedError, KeyAlreadyRevokedError, KeyNotFoundError, LastActiveKeyError.
Enums
| Enum | Members |
|---|
HttpMethod | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS |
CallerType | SERVICE, AGENT |
Provider | Slug enum for the OAuth provider catalog. |
Express recipe
import express from "express";
import { AsyncLocalStorage } from "node:async_hooks";
import { App, HttpMethod } from "@alter-ai/alter-sdk";
const jwt = new AsyncLocalStorage<string>();
const alter = new App({
apiKey: process.env.ALTER_API_KEY!,
userTokenGetter: () => jwt.getStore()!,
});
const app = express();
app.get("/api/events", authenticated, async (req, res, next) => {
try {
await jwt.run(req.user.jwt, async () => {
const r = await alter.request(
HttpMethod.GET,
"https://www.googleapis.com/calendar/v3/calendars/primary/events",
{ provider: "google" },
);
res.json(await r.json());
});
} catch (e) { next(e); }
});