Skip to content

Reference

Client

App and Agent — constructors, properties, lifecycle, withConstraints, getAgent.

import { App, Agent } from "@alter-ai/alter-sdk";

The SDK exposes two top-level client classes. They share most of the request surface but model two different principal kinds on the wire. Pick the right class for the workload:

  • App — application-side. Holds the application’s API key (alter_rk_…). Provisions agents, mints Connect sessions, calls provider APIs under any stored grant.
  • Agent — workload-side. Holds an agent’s API key (alter_ak_…). Can only reach credentials delegated or directly bound to the agent.

Methods that do not apply to a principal kind are intentionally absent from that class — TypeScript flags the missing methods at compile time.

The application-side client.

import { App, CallerType } from "@alter-ai/alter-sdk";
const app = new App({
apiKey: process.env.ALTER_API_KEY!,
timeout: 30_000,
caller: "customer-portal",
callerType: CallerType.SERVICE,
userTokenGetter: () => getCurrentUserJwt(),
});
try {
const page = await app.listGrants();
console.log(page.total);
} finally {
await app.close();
}
new App(options: AppOptions)
OptionTypeDefaultDescription
apiKeystringApplication API key (alter_rk_…). Required.
timeoutnumber30000HTTP timeout in milliseconds.
callerstringCaller identifier for audit attribution.
callerTypeCallerType | "agent" | "service"SERVICEDistinguishes backend services from AI agents. Only effective when caller is set.
userTokenGetter() => string | Promise<string>Supplies the current end-user JWT for user-scoped identity resolution. A per-call userToken overrides it; headless system-grant resolution can omit it.
loggerAlterLoggerglobal consolePluggable logger. See AlterLogger.

Throws AlterValueError when the constructor argument is not an object. Throws AlterSDKError if apiKey is missing or malformed, if userTokenGetter is present but not callable at runtime, or if the configured backend URL is invalid.

PropertyTypeDescription
actorIdstring | nullBackend-resolved actor UUID. Populated after the first authenticated call.
lastRetryInfoRetryInfo | nullToken-refresh retry metadata from the most recent request() call.
lastRateLimitRateLimitSnapshot | nullLatest complete Alter rate-limit reading from a metered backend response. null means unknown, never unlimited.
baseUrlstringBackend the client is pinned to. Diagnostic accessor.
agentsAgentsNamespaceManaged agent CRUD.
keysKeysNamespaceScoped sub-key lifecycle.
scopesScopesNamespaceScope catalog discovery.
oauthProvidersOAuthProvidersNamespaceOAuth provider catalog discovery.
providerSpecsProviderSpecsNamespaceProvider operation discovery.
spansSpansNamespaceUser-defined trace-span submission.

The App class exposes:

The workload-side client.

import { Agent } from "@alter-ai/alter-sdk";
const agent = new Agent({
apiKey: process.env.AGENT_API_KEY!,
caller: "research-agent",
userTokenGetter: () => getCallingUserJwt(),
});
try {
const info = await agent.me();
console.log(info.status);
} finally {
await agent.close();
}
new Agent(options: AgentOptions)
OptionTypeDefaultDescription
apiKeystringAgent API key (alter_ak_…). Required.
timeoutnumber30000HTTP timeout in milliseconds.
callerstringCaller identifier for audit attribution.
userTokenGetter() => string | Promise<string>Resolves the calling end-user JWT. Used for delegation disambiguation when the agent holds delegations from multiple users on the same provider.
loggerAlterLoggerglobal consolePluggable logger.

callerType is forced to AGENT on this class. Throws AlterValueError when the constructor argument is not an object or apiKey is not a non-empty string. Throws AlterSDKError if a non-empty apiKey is malformed, if userTokenGetter is present but not callable at runtime, or if the configured backend URL is invalid.

PropertyTypeDescription
actorIdstring | nullBackend-resolved actor UUID.
lastRetryInfoRetryInfo | nullToken-refresh retry metadata.
lastRateLimitRateLimitSnapshot | nullLatest complete Alter rate-limit reading from a metered backend response. null means unknown, never unlimited.
baseUrlstringBackend the client is pinned to.
keysKeysNamespaceScoped sub-key lifecycle.
scopesScopesNamespaceScope catalog discovery.
oauthProvidersOAuthProvidersNamespaceOAuth provider catalog discovery.
providerSpecsProviderSpecsNamespaceProvider operation discovery.
spansSpansNamespaceUser-defined trace-span submission.

Agent has no agents namespace — agent CRUD is application-scoped.

The Agent class exposes:

Resolve the canonical identity set for a request.

// App
async resolveIdentity(options?: {
userToken?: string;
includeProfile?: boolean;
}): Promise<IdentityContext>
// Agent
async resolveIdentity(options?: {
userToken?: string;
appUserId?: string;
includeProfile?: boolean;
}): Promise<IdentityContext>

App falls back to its configured userTokenGetter when userToken is omitted. Agent requires an explicit userToken or appUserId to resolve an end user; its constructor getter is not forwarded by this identity method. The agent-only appUserId shortcut succeeds only when an existing consent edge authorizes that agent for the user. With no user selector, the method returns the headless app or agent identity.

includeProfile defaults to false; enable it only when email and displayName are needed. Returns IdentityContext. Invalid options throw AlterValueError; identity or backend failures use the standard typed error hierarchy.

Mint a short-lived, identity-only assertion for a downstream audience.

// App
async assertIdentity(options: {
audience: string;
userToken?: string;
ttlSeconds?: number;
includeProfile?: boolean;
}): Promise<IdentityAssertion>
// Agent
async assertIdentity(options: {
audience: string;
userToken?: string;
appUserId?: string;
ttlSeconds?: number;
includeProfile?: boolean;
}): Promise<IdentityAssertion>

audience is a required URI with a scheme and at most 512 characters. ttlSeconds defaults to 120 and must be an integer from 10 through 300. User selection follows resolveIdentity(), including the agent-only appUserId shortcut and the App getter fallback. Returns IdentityAssertion.

App only. Validate an incoming end-user bearer token and return its verified subject.

async verifyUserToken(token: string): Promise<string | null>

This method is fail-closed: it returns null for a blank or untrusted token, transport failure, malformed response, unexpected status, or a closed client. Treat every null result as an authentication rejection.

Return a constrained sibling client. Macaroon-style attenuation that can only narrow access, never broaden it. Two attenuations are available, and at least one of scopes / rule must be supplied:

  • scopes narrows the scope set. The argument must be a subset of the parent client’s effective scope set; the backend enforces the narrowing on every authenticated call.
  • rule attaches an optional request rule, evaluated server-side, that can only further restrict each request the returned client makes — it can never widen access. Supported shapes include json_match deny rules, require_approval HITL rules, and content_match rules built with contentMatchRule(...). A raw-token retrieval carries no request method/URL, so method-keyed conditions are treated as satisfied there (a raw token can issue any method) — scope method restrictions through the proxy surface, which carries the request method. If a request is frozen for human approval (HITL 202), the per-request rule is not re-evaluated on the deferred post-approval execution — it cannot widen access (the rule did not match the frozen request, and the grant plus every stored policy rule still bound the execution), but prefer a stored rule when the constraint must hold at execute time.
withConstraints(options: { scopes?: readonly string[]; rule?: RequestRule }): App
withConstraints(options: { scopes?: readonly string[]; rule?: RequestRule }): Agent

RequestRule is shaped:

interface RequestRule {
ruleType: string;
ruleBody: Record<string, unknown>;
}

For the json_match rule type, ruleBody is { when: { <attr>: string | string[] }, effect: "deny" }, where <attr> is one of method, provider_id, app_id, agent_id, api_key_id, environment, client_ip, resource_kind, and effect is always "deny". For operation-aware policies, prefer contentMatchRule(...); it emits the snake_case content_match body, validates parameter-condition shape, requires redactFields iff effect: "redact", and requires maxSessionAgeSeconds iff effect: "step_up" as an integer in [1, 86400]. The ruleBody field names are the rule grammar’s own — they are not camelCased.

Both the parent and constrained sibling share the same internal transport clients, signing state, and background-task sets. Closing the parent also makes the sibling unusable; closing the sibling alone leaves the parent untouched.

withConstraints() cannot be chained — calling it on an already-constrained client throws AlterValueError. It also throws AlterValueError if neither scopes nor rule is supplied. Calling it on a closed client throws AlterSDKError.

import { contentMatchRule } from "@alter-ai/alter-sdk";
const readOnly = app.withConstraints({ scopes: ["grants:read"] });
const page = await readOnly.listGrants();
// Attach a json_match deny rule to every request from this constrained client:
const restricted = app.withConstraints({
rule: {
ruleType: "json_match",
ruleBody: { when: { method: "POST" }, effect: "deny" },
},
});
// Attach an operation-aware content rule. The helper validates that
// maxSessionAgeSeconds is an integer in [1, 86400].
const freshSession = app.withConstraints({
rule: contentMatchRule({
operations: ["resource.create"],
effect: "step_up",
maxSessionAgeSeconds: 300,
}),
});

Return an Agent instance that impersonates a named managed agent for audit attribution.

getAgent(agentId: string): Agent
ParameterTypeDescription
agentIdstringUUID of the managed agent to impersonate.

Throws AlterValueError if agentId is not a valid UUID, or if the App has been closed.

const research = app.getAgent("d3f4e5a6-7b8c-9d0e-1f2a-3b4c5d6e7f80");
try {
await research.request("GET", url, {
provider: "<provider-id>",
userToken,
});
} finally {
await research.close();
}

Complete any pending work, then mark the client closed so subsequent operations fail deterministically.

async close(): Promise<void>
async [Symbol.asyncDispose](): Promise<void>

Both App and Agent expose close() and Symbol.asyncDispose. The SDK does not auto-close on process exit; call it explicitly (typically from a try/finally) or use await using in runtimes that support explicit resource management:

const app = new App({ apiKey });
try {
// ...
} finally {
await app.close();
}
await using scopedApp = new App({ apiKey });
// scopedApp.close() runs when the scope exits.

close() is idempotent. Closing a root client drains the shared background-task set; closing a constrained sibling only marks that sibling closed, so close the root before process shutdown. Subsequent calls on a closed client throw AlterSDKError.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.