Skip to content

Reference

alter sdk-passthrough

Make one-off runtime provider requests and inspect resolved identity.

sdk-passthrough is an escape hatch for making one-off runtime SDK calls — the kind of thing you’d otherwise do with the runtime SDK’s request() or resolveIdentity(). It exists for debugging (“fetch a token for this grant and hit the provider once to see what comes back”, “which identity does the backend resolve for this key?”), deliberately namespaced so it reads as “I’m doing something unusual” rather than blending in with the management commands.

alter sdk-passthrough request <grant-id> --url <url> [--method ...] [--body ...] [--header ...] [--api-key <key>]
alter sdk-passthrough resolve-identity [--user-token <jwt>] [--app-user-id <uuid>] [--include-profile] [--agent-only] [--api-key <key>]

Make a single authenticated call against a grant. The grant must be active and owned by the calling app.

Required key scopes. request retrieves the credential over the token channel, calls the provider directly, then reports the outcome over the audit channel — the key needs tokens:retrieve and audit:emit. Without audit:emit the provider call still succeeds but its api-call audit row is dropped, and the CLI prints Audit log failed with status 403 (non-fatal) naming the missing scope. (Proxied calls made with proxy_request() / proxyRequest() in the SDKs need proxy:execute instead.)

FlagRequiredDescription
--url <url>yesThe full provider URL to call (e.g. https://www.googleapis.com/oauth2/v3/userinfo). Must be http:// or https://. There is no relative-path resolution — pass the complete URL.
--method <method>noHTTP method (default GET). One of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.
--body <json>noJSON text parsed locally, then passed as the provider request body. Invalid JSON exits with usage code 2 before any request.
--header <kv>noExtra header in Name: value form. Repeatable; a later occurrence of the same header name overwrites the earlier value. Reserved headers (the Authorization header and Alter’s own request headers) are rejected — Alter injects the credential for you.
--api-key <key>noRuntime API key, if not using ALTER_API_KEY. The CLI validates the complete format locally: scoped keys use a supported prefix, 22 lowercase-base32 random characters, and a matching 6-character CRC; legacy alter_key_… keys use exactly 40 URL-safe body characters. PAT plaintext (alter_pat_…) is rejected — use alter auth login for that. Inline values print a shell-history warning.

The response body goes to stdout (pipe it into jq); the status line and response headers go to stderr, so the body stays clean for downstream tools.

A provider error keeps the same layout: the HTTP <status> line and an explanatory message go to stderr, the provider’s error body goes to stdout, and the command exits 1. The body is the diagnostic (a validation error naming the field, a “database resuming” notice, a rejected credential), so a script that keys off the exit code can still read it.

Stderr also carries an alter-rate-limit: line reporting Alter’s own remaining budget for the key — alter-rate-limit: 994/1000 remaining, resets 2027-01-15T08:00:00.000Z. It is namespaced so it cannot be confused with the provider’s response headers printed beside it, which describe a different budget entirely. The line is omitted when the backend reported no budget for the call.

Terminal window
export ALTER_API_KEY="alter_rk_..."
alter sdk-passthrough request <grant-id> \
--url https://www.googleapis.com/oauth2/v3/userinfo \
| jq .
# With a body
alter sdk-passthrough request <grant-id> \
--method POST \
--url https://slack.com/api/chat.postMessage \
--body '{"channel":"#general","text":"hello"}'

When the grant belongs to a stub application, the call is served by the stub provider rather than the real one. If that application is reached on a deployment where stub-provider mode is not enabled, the request is refused with conflict code 5 and a message explaining that the application cannot be served there.

That refusal is terminal, not transient: it holds for as long as the application and the deployment disagree, so retrying the same call can never succeed. Scripts should treat exit 5 here as “this application cannot run against this deployment” and stop, rather than backing off and retrying.

Resolve the canonical identity context for the runtime key (the SDK’s resolveIdentity() — requires the identity:resolve scope on the key). Prints the identity JSON plus, when the key’s identity is eligible, the derived private memory partition (memory_scope), so an operator can verify exactly which app / agent / end-user identity the backend resolves and which partition the SDK’s ScopedMemoryStore would enforce for it. The partition is diagnostic metadata, never a credential.

FlagRequiredDescription
--user-token <jwt>noEnd-user IDP JWT to resolve. Validated against the application’s configured identity provider. Because this value is on argv, the CLI warns that it may appear in shell history/process listings; prefer an environment variable or file-backed command substitution.
--app-user-id <uuid>noAgent-only consent-edge shortcut: resolve this end user without a token. Requires a key bound to a managed agent that holds a delegation from that user — keys minted on the agent-keys surface currently use the legacy alter_key_… prefix; alter_ak_… keys are accepted too. The backend authorizes on the key’s agent binding, not its prefix; the CLI only rejects definitively non-agent prefixes (alter_rk_…, alter_dk_…, alter_pk_…) locally.
--agent-onlynoDerive the headless application + agent partition for a verified agent key with no represented user. Off by default; without it an active represented user is required. The flag can never drop a user from an identity that represents one (memory_user_cannot_be_dropped).
--include-profilenoOpt into PII (email + display name) on the response. Off by default.
--api-key <key>noRuntime API key, if not using ALTER_API_KEY. Same rules as request above.

With neither --user-token nor --app-user-id, the call is headless and resolves the key’s own identity — the application, plus the agent identity when the key is bound to a managed agent.

The JSON document also carries a rate_limit object — limit, remaining and reset_at — reporting Alter’s own remaining budget for the key after this call, so a script can throttle itself before it is throttled. It is null when the backend reported no budget: unknown, never unlimited. See Errors for the header contract behind it.

There is deliberately no assert-identity counterpart: assertion-minting produces a signed credential-shaped token whose only consumers are programmatic verifiers, so it ships on the SDKs only.

Terminal window
# An agent-BOUND key (minted on the agent-keys surface).
export ALTER_API_KEY="alter_key_..."
# Which identity does this key resolve for this user's JWT?
alter sdk-passthrough resolve-identity --user-token "$USER_JWT" | jq .
# Consent-edge shortcut (agent key + a delegation from this user)
alter sdk-passthrough resolve-identity --app-user-id 22222222-2222-2222-2222-222222222222 | jq .memory_scope
# Headless agent: the explicit application + agent partition
alter sdk-passthrough resolve-identity --agent-only | jq .memory_scope

Two fields describe the partition, and ineligible identities still print successfully for inspection:

FieldShapeMeaning
memory_scopeobject or nullThe SDK’s MemoryScope as JSON: the versioned v2 namespace array, the slash-joined partition_key, and the user_key / agent_key / app_key correlation labels. null when no private partition can be derived for this identity.
memory_scope_error{ "code": ..., "message": ... } or nullWhy no partition was derived. code is the SDK’s stable MemoryIsolationError.code (for example memory_agent_required for an application-only key, or memory_user_required for a headless agent key without --agent-only); scripts branch on code, never on message. null when memory_scope is set.
Terminal window
# A script branches on the stable code, never on the message text.
alter sdk-passthrough resolve-identity --output json \
| jq -r '.memory_scope_error.code // "ok"'

These keys are metadata, not credentials; never accept this JSON as remote identity proof, and never derive ownership from conversation, task or run labels. The full code table is in Errors; the enforcement model is in memory enforcement and migration.

Debug a grant from scratch

Terminal window
# 1. Mint a runtime key (printed once) and export it.
alter keys mint --name debug --scopes tokens:retrieve --output json
export ALTER_API_KEY="alter_rk_..."
# 2. Call the provider through the grant and inspect the response.
alter sdk-passthrough request <grant-id> --url https://www.googleapis.com/oauth2/v3/userinfo | jq .

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.