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] [--api-key <key>]request
Section titled “request”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.)
| Flag | Required | Description |
|---|---|---|
--url <url> | yes | The 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> | no | HTTP method (default GET). One of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. |
--body <json> | no | JSON text parsed locally, then passed as the provider request body. Invalid JSON exits with usage code 2 before any request. |
--header <kv> | no | Extra 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> | no | Runtime 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.
export ALTER_API_KEY="alter_rk_..."
alter sdk-passthrough request <grant-id> \ --url https://www.googleapis.com/oauth2/v3/userinfo \ | jq .
# With a bodyalter sdk-passthrough request <grant-id> \ --method POST \ --url https://slack.com/api/chat.postMessage \ --body '{"channel":"#general","text":"hello"}'Stub applications
Section titled “Stub applications”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-identity
Section titled “resolve-identity”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 the derived memory-scope partition keys, so an operator can verify exactly which app / agent / end-user identity the backend resolves before wiring those keys into a memory store.
| Flag | Required | Description |
|---|---|---|
--user-token <jwt> | no | End-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> | no | Agent-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. |
--include-profile | no | Opt into PII (email + display name) on the response. Off by default. |
--api-key <key> | no | Runtime 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.
# 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_scopeRecipes
Section titled “Recipes”Debug a grant from scratch
# 1. Mint a runtime key (printed once) and export it.alter keys mint --name debug --scopes tokens:retrieve --output jsonexport 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 .Related
Section titled “Related”alter keys mint— create the runtime key this command uses- Python SDK / TypeScript SDK — the supported way to make requests from application code