Reference
Types
Pydantic models, enums, and discriminated unions returned across the SDK surface.
Fields are snake_case and match the wire format. Most Pydantic models are frozen. APICallAuditLog, UserSpan, and EmitSpansResult are mutable Pydantic models; RequestRule and ContentParamCondition are TypedDict shapes.
from alter_sdk import ( TokenResponse, GrantInfo, ConnectSession, ConnectResult, UnifiedGrantListResult, OAuthGrantItem, ManagedSecretGrantItem, GrantListItem, GrantPolicy, GrantPolicyInput, ConnectFailedGrant, CreateGrantResult, RevokeGrantResult, DelegationResult, Principal, PrincipalType, NonGroupPrincipalType, UserPrincipal, GroupPrincipal, SystemPrincipal, AgentPrincipal, AgentInfo, AgentCreateResult, AgentListResult, AgentKey, AgentKeyList, AgentKeyMintResult, APIKeyInfo, MintedKey, ScopeCatalog, ResourceScopes, ApprovalGate, ApprovalGateStatus, ApprovalResult, ApprovalStatus, ApprovalStatusValue, PendingApproval, AuthResult, AuthSession, APICallAuditLog, OAuthProviderCatalog, OAuthProviderCatalogItem, OAuthProviderScopeInfo, ProviderSpec, ProviderSpecKind, SpecOperation, SpecOperationsPage, SpecOperationDetail, RetryInfo, RetryErrorInfo, RateLimitSnapshot, RequestRule, ContentMatchEffect, ContentMatchFamily, ContentParamCondition, ContentParamOp, content_match_rule, UserSpan, EmitSpansResult, IdentityContext, IdentityTrace, MemoryScope, IdentityAssertion, HttpMethod, CallerType, Provider,)HttpMethod
Section titled “HttpMethod”HttpMethod.GET # "GET"HttpMethod.POST # "POST"HttpMethod.PUT # "PUT"HttpMethod.PATCH # "PATCH"HttpMethod.DELETE # "DELETE"HttpMethod.HEAD # "HEAD"HttpMethod.OPTIONS # "OPTIONS"Plain strings are also accepted by every SDK method that takes method.
CallerType
Section titled “CallerType”CallerType.AGENT # "agent"CallerType.SERVICE # "service"App defaults to SERVICE. Agent pins AGENT internally.
Provider
Section titled “Provider”Static enum of OAuth provider identifiers. It contains the provider ids available when the SDK version was published. Methods also accept plain strings for forward compatibility with provider ids added after that release.
Token & retry
Section titled “Token & retry”TokenResponse
Section titled “TokenResponse”OAuth token response. The plaintext access token is NOT stored as a readable attribute — the SDK injects it into outbound requests but never exposes it.
| Field | Type | Description |
|---|---|---|
grant_id | str | Grant id that provided this token. |
token_type | str | Token type. Default "Bearer". |
expires_in | int | None | Seconds until expiry. |
expires_at | datetime | None | Absolute instant this credential stops working — the binding value: the earlier of the provider token’s own expiry and the grant’s TTL. Caching past it produces an authorization failure, not a refresh. |
token_expires_at | datetime | None | The provider token’s own expiry, independent of the grant’s TTL. The only clock a refresh can move. |
grant_expires_at | datetime | None | The grant’s own TTL instant (the access duration chosen at consent); None when the grant has no TTL. |
scopes | list[str] | OAuth scopes granted. |
provider_id | str | None | Provider id. |
scope_mismatch | bool | True when the backend has flagged this grant as requiring re-authorization. |
injection_header | str | Credential-injection header name. Excluded from model_dump(). |
injection_format | str | Credential-injection value format. Excluded from model_dump(). |
additional_credentials | dict[str, str] | None | Additional non-exposed credential metadata for multipart authentication. Excluded from model_dump(). |
additional_injections | list[dict[str, str]] | None | Additional header or query injection rules. Excluded from model_dump(). |
Methods: is_expired(buffer_seconds=0) -> bool, needs_refresh(buffer_seconds=300) -> bool.
The two answer different questions and read different fields. is_expired() reads expires_at — whether the credential still works. needs_refresh() reads token_expires_at — whether refreshing would help.
They diverge whenever the grant’s TTL is the earlier of the two clocks: the credential is about to stop working, but a refresh cannot extend permission and the correct response is re-consent. Compare grant_expires_at to tell which clock is binding.
RateLimitSnapshot
Section titled “RateLimitSnapshot”Frozen snapshot exposed as last_rate_limit on App, Agent, and constrained clients.
| Field | Type | Description |
|---|---|---|
limit | int | Binding requests-per-minute ceiling for the response. |
remaining | int | Requests remaining against that binding ceiling. |
reset_at | datetime | Aware UTC instant when the reported window resets. |
The value is None until a metered response supplies the standard RateLimit-Policy / RateLimit pair or the compatibility X-RateLimit-* triple. A later unmetered response does not erase the last reading. Provider response headers are not captured because they describe the provider’s budget, not Alter’s.
RetryInfo
Section titled “RetryInfo”Attached to response.retry_info when the backend refreshed a token after transient failures.
| Field | Type | Description |
|---|---|---|
total_attempts | int | Total attempts made. |
successful_attempt | int | Which attempt succeeded (0 if none). |
errors | tuple[RetryErrorInfo, ...] | Failed attempts (immutable). |
RetryErrorInfo
Section titled “RetryErrorInfo”| Field | Type | Description |
|---|---|---|
attempt | int | 1-based attempt index. |
error | str | Error message, truncated to 500 chars. |
error_type | str | Error classification. |
delay_s | float | Sleep duration before the next attempt. |
permanent | bool | Whether the error is permanent. |
Grants
Section titled “Grants”GrantInfo
Section titled “GrantInfo”Metadata shape returned by App.mint_grant(), and retained for legacy grant responses. App.list_grants() and Agent.list_grants() return OAuthGrantItem / ManagedSecretGrantItem through the unified list instead.
| Field | Type |
|---|---|
grant_id | str |
provider_id | str |
scopes | list[str] |
account_identifier | str | None |
account_display_name | str | None |
status | str |
scope_mismatch | bool |
expires_at | str | None |
created_at | str |
last_used_at | str | None |
principal_type | "user" | "group" | "system" | "agent" |
label | str | None |
credential_id | str | None |
grant_policy | dict[str, Any] | None |
UnifiedGrantListResult
Section titled “UnifiedGrantListResult”Returned by App.list_grants() and Agent.list_grants().
| Field | Type | Description |
|---|---|---|
grants | list[GrantListItem] | Discriminated-union items. |
total | int | Total matching rows. |
limit | int | Page size. |
offset | int | Page offset. |
has_more | bool | Canonical “more pages” signal. Prefer this over computing offsets against total. |
GrantListItem
Section titled “GrantListItem”Discriminated union by grant_kind:
GrantListItem = OAuthGrantItem | ManagedSecretGrantItemOAuthGrantItem
Section titled “OAuthGrantItem”grant_kind="oauth".
| Field | Type | Description |
|---|---|---|
grant_kind | "oauth" | Discriminator. |
grant_id | str | |
provider_id | str | |
scopes | list[str] | |
account_identifier | str | None | |
account_display_name | str | None | |
status | str | |
scope_mismatch | bool | |
needs_reconnect | bool | Connection health (a separate axis from status): True when the grant is active but its provider token broke and needs re-auth. A call against it may raise ReAuthRequiredError (a recoverable break self-heals transparently on the next call); the flag clears on a successful refresh or a reconnect. |
expires_at | str | None | The provider token’s expiry (auto-refreshed — never a grant terminal). |
grant_expires_at | str | None | The grant’s own TTL-policy expiry (the access duration chosen at consent). A different axis from expires_at; None = perpetual. status reads "expired" once this instant passes, even before the record is lazily flipped. |
created_at | str | |
last_used_at | str | None | |
principal_type | "user" | "system" | "agent" | The "agent" value marks a delegation child surfaced as its own row in the operator list. |
access_via | "ownership" | "oauth_delegation" | How the caller reached the grant. |
delegated_at | str | None | Agent-branch field: when the calling agent’s delegation row was created. |
delegated_agent_ids | list[str] | Operator-branch field: agent UUIDs this grant is currently delegated to. |
label | str | None | Stable sibling-grant address within the credential. |
credential_id | str | None | Shared credential id used to group sibling grants. |
grant_policy | dict[str, Any] | None | Per-grant policy. |
parent_grant_id | str | None | The grant this one was minted under. None marks a root (a directly-consented grant). |
depth | int | Hop distance from the root (0 at the root). With parent_grant_id, lets a caller reconstruct the delegation tree from the flat list. |
delegable | bool | Whether this grant may be re-delegated onward to another agent. Always present (defaults False). |
scope_constraint | list[str] | None | The narrowed provider-scope subset this delegated grant is clamped to. None = inherits the parent/credential scope. |
ManagedSecretGrantItem
Section titled “ManagedSecretGrantItem”grant_kind="managed_secret". Returned for any managed-secret grant the caller can reach — an operator (App) list surfaces every principal kind the app owns; an agent list surfaces its own and delegated grants.
| Field | Type |
|---|---|
grant_kind | "managed_secret" |
grant_id | str |
managed_secret_id | str |
managed_secret_slug | str |
managed_secret_name | str |
agent_id | str | None |
label | str | None |
status | str |
account_identifier | str | None |
grant_policy | dict | None |
expires_at | str | None |
grant_expires_at | str | None |
created_at | str |
last_used_at | str | None |
principal_type | "user" | "group" | "system" | "agent" |
access_via | "ownership" | "ms_delegation" |
delegated_at | str | None |
parent_grant_id | str | None |
depth | int |
delegable | bool |
scope_constraint | list[str] | None |
RevokeGrantResult
Section titled “RevokeGrantResult”| Field | Type |
|---|---|
success | bool |
message | str |
grant_id | str |
revoked_at | str |
CreateGrantResult
Section titled “CreateGrantResult”| Field | Type |
|---|---|
grant_id | str |
principal_type | "user" | "group" | "system" | "agent" |
label | str | None |
created_at | str |
DelegationResult
Section titled “DelegationResult”Returned by agent.delegate() — the child grant minted onto another agent.
| Field | Type | Description |
|---|---|---|
grant_id | str | The new child grant. |
grant_kind | "oauth" | "managed_secret" | Which credential family the child belongs to. |
parent_grant_id | str | The held grant the child was minted under (always set — a delegation result is always a child). |
depth | int | The child’s hop distance from the root (always >= 1). |
delegable | bool | Whether the recipient may re-delegate this child onward. |
status | str | Grant status (e.g. "active"). |
expires_at | str | None | Child expiry, clamped to the parent’s lifetime. |
GrantPolicy
Section titled “GrantPolicy”| Field | Type | Description |
|---|---|---|
expires_at | str | None | Hard expiry timestamp (ISO 8601 UTC). |
created_by | str | None | "developer" or "end_user". |
created_at | str | None |
GrantPolicyInput
Section titled “GrantPolicyInput”Input model accepted by Connect and sibling-grant minting methods.
| Field | Type | Description |
|---|---|---|
expires_at | str | None | Hard expiry timestamp (ISO 8601 UTC). |
max_ttl_seconds | int | None | Positive maximum TTL in seconds. |
default_ttl_seconds | int | None | Positive pre-selected TTL; must not exceed max_ttl_seconds. |
Principals (discriminated union)
Section titled “Principals (discriminated union)”Principal = UserPrincipal | GroupPrincipal | SystemPrincipal | AgentPrincipalThe discriminator is type.
PrincipalType is Literal["user", "group", "system", "agent"].
NonGroupPrincipalType is Literal["user", "system", "agent"] and is used by
OAuth grant models, where group principals are not supported.
UserPrincipal
Section titled “UserPrincipal”| Field | Type | Description |
|---|---|---|
type | "user" | Discriminator. |
user_token | str | IDP JWT (1-8192 chars). |
label | str | 1-255 chars. |
GroupPrincipal
Section titled “GroupPrincipal”| Field | Type | Description |
|---|---|---|
type | "group" | Discriminator. |
external_group_id | str | 1-255 chars. |
idp_id | str | Identity provider id (UUID), 1-255 chars. |
label | str | 1-255 chars. |
SystemPrincipal
Section titled “SystemPrincipal”| Field | Type | Description |
|---|---|---|
type | "system" | Discriminator. |
label | str | None | Optional, max 255 chars. |
AgentPrincipal
Section titled “AgentPrincipal”| Field | Type | Description |
|---|---|---|
type | "agent" | Discriminator. |
label | str | None | Optional, max 255 chars. |
The agent is resolved from the API-key signature server-side — there is no agent_id on the wire.
Connect / Auth
Section titled “Connect / Auth”ConnectSession
Section titled “ConnectSession”| Field | Type |
|---|---|
session_token | str |
connect_url | str |
expires_in | int |
expires_at | str |
scope_constraint_warnings | list[str] |
scope_constraint_warnings (default []) carries one diagnostic per provider whose required scopes the requested scope_constraint admits nothing against — the session still succeeds, but a grant minted from it would deny every call until the constraint is widened.
ManagedSecretConnectSession
Section titled “ManagedSecretConnectSession”Same shape as ConnectSession minus scope_constraint_warnings. __repr__ redacts connect_url (it carries the session token in the URL fragment).
| Field | Type |
|---|---|
session_token | str |
connect_url | str |
expires_in | int |
expires_at | str |
ConnectResult
Section titled “ConnectResult”| Field | Type |
|---|---|
grant_id | str |
provider_id | str |
account_identifier | str | None |
scopes | list[str] |
grant_policy | GrantPolicy | None |
failed_grants | list[ConnectFailedGrant] |
ConnectFailedGrant
Section titled “ConnectFailedGrant”One provider whose newly authorized grant was revoked because the selected usage limits could not be applied.
| Field | Type | Description |
|---|---|---|
provider_id | str | Provider id. |
reason | str | Machine-readable failure reason. Unknown values are retained. |
message | str | Operator-safe failure message. |
AuthResult
Section titled “AuthResult”| Field | Type | Description |
|---|---|---|
user_token | str | IDP JWT bearer token. |
user_info | dict[str, Any] | User info from IDP (sub, email, name). |
AuthSession
Section titled “AuthSession”Returned by create_auth_session(). __repr__ redacts auth_url (it carries the session token as the OIDC state parameter).
| Field | Type | Description |
|---|---|---|
session_token | str | Handle to poll with poll_auth_session(). Persistable. |
auth_url | str | IDP sign-in URL to hand to the end user. Never log it. |
expires_in | int | Seconds until the session expires. |
expires_at | str | ISO 8601 expiry timestamp. |
Provider catalog
Section titled “Provider catalog”OAuthProviderCatalog
Section titled “OAuthProviderCatalog”Returned by oauth_providers.list().
| Member | Type | Description |
|---|---|---|
providers | dict[str, OAuthProviderCatalogItem] | Connectable providers, keyed by id ("<provider_id>", …). |
get_default_scopes(provider) | tuple[str, ...] | Default scopes for a provider, or () if unknown. Accepts a string or Provider member. |
get_required_scopes(provider) | tuple[str, ...] | Required scopes for a provider, or () if unknown. |
OAuthProviderCatalogItem
Section titled “OAuthProviderCatalogItem”| Field | Type | Description |
|---|---|---|
id | str | Provider id. |
name | str | Internal provider name. |
display_name | str | Human-readable name. |
category | str | None | Provider category. |
description | str | None | Short description. |
logo_url | str | None | Logo URL. |
supports_refresh | bool | Provider issues refresh tokens. |
supports_pkce | bool | Provider supports PKCE. |
available_scopes | dict[str, OAuthProviderScopeInfo] | All requestable scopes, keyed by scope string. |
default_scopes | list[str] | Scopes pre-selected in a Connect flow. |
required_scopes | list[str] | Scopes that are always requested. |
status | str | Always "active" (only connectable providers are returned). |
OAuthProviderScopeInfo
Section titled “OAuthProviderScopeInfo”| Field | Type | Description |
|---|---|---|
description | str | What the scope grants. |
required | bool | Scope is always requested. |
is_default | bool | Scope is pre-selected by default. |
Provider operation specs
Section titled “Provider operation specs”ProviderSpecKind
Section titled “ProviderSpecKind”ProviderSpecKind = Literal["oauth", "managed"]ProviderSpec
Section titled “ProviderSpec”| Field | Type | Description |
|---|---|---|
provider_kind | str | Provider family ("oauth" or "managed"). |
provider_id | str | Provider identifier. |
version | int | Monotonic stored-spec version. |
provenance | str | Ingestion source. |
source_url | str | None | Upstream spec URL when known. |
content_hash | str | Hash of the ingested content. |
operation_count | int | Number of operations in the spec. |
fetched_at | datetime | Most recent ingestion time. |
changed_at | datetime | Time the active content version last changed. |
title | str | None | Spec title when present. |
spec_version | str | None | Upstream spec version when present. |
SpecOperation
Section titled “SpecOperation”| Field | Type |
|---|---|
operation_id | str |
method | str |
path_template | str |
summary | str | None |
SpecOperationsPage
Section titled “SpecOperationsPage”| Field | Type | Description |
|---|---|---|
items | list[SpecOperation] | Operations on this page. |
total | int | Total matching operations. |
limit | int | Page size. |
offset | int | Page offset. |
has_more | bool | Whether another page exists. |
spec | ProviderSpec | Metadata for the active spec that served the page. |
SpecOperationDetail
Section titled “SpecOperationDetail”| Field | Type |
|---|---|
operation_id | str |
method | str |
path_template | str |
summary | str | None |
params_schema | list[dict[str, Any]] | None |
request_schema | dict[str, Any] | None |
response_schema | dict[str, Any] | None |
spec | ProviderSpec |
See Provider discovery for the namespace methods.
Agents
Section titled “Agents”AgentInfo
Section titled “AgentInfo”The managed-agent record. PII (HITL approver list) is redacted server-side.
| Field | Type | Description |
|---|---|---|
id | UUID | |
name | str | |
display_name | str | None | |
type | "agent" | "service" | |
status | "active" | "inactive" | "revoked" | |
scopes | dict[str, Any] | Per-provider scope allowlist. |
scopes_pending | dict[str, Any] | None | Pending narrowing (Phase G). |
scopes_applies_at | datetime | None | |
policy | dict[str, Any] | Validated policy block. |
metadata | dict[str, Any] | |
rate_limit_per_minute | int | None | |
version | int | Monotonic version counter. |
created_at | datetime | |
last_used_at | datetime | None | |
parent_agent_id | UUID | None | Reserved; not currently exposed. |
AgentCreateResult
Section titled “AgentCreateResult”Extends AgentInfo. Adds:
| Field | Type | Description |
|---|---|---|
api_key | str | None | Plaintext API key. Shown ONCE. None on idempotency replay (branch on api_key is None). |
key_id | UUID | The api_keys row id backing this agent. |
AgentListResult
Section titled “AgentListResult”| Field | Type |
|---|---|
agents | list[AgentInfo] |
total | int |
limit | int |
offset | int |
has_more | bool |
AgentKey
Section titled “AgentKey”| Field | Type | Description |
|---|---|---|
key_id | UUID | |
key_prefix | str | Display-only key prefix. |
name | str | Auto-generated {agent_name}-{N} at mint time. |
created_at | datetime | |
deprecated_at | datetime | None | |
revoked_at | datetime | None | |
last_used_at | datetime | None |
Derived property: status — "active" \| "deprecated" \| "revoked".
AgentKeyMintResult
Section titled “AgentKeyMintResult”Extends AgentKey. Adds:
| Field | Type | Description |
|---|---|---|
api_key | str | None | Plaintext. Shown ONCE. |
AgentKeyList
Section titled “AgentKeyList”| Field | Type |
|---|---|
items | list[AgentKey] |
Keys & Scopes
Section titled “Keys & Scopes”APIKeyInfo
Section titled “APIKeyInfo”Read-only view of a scoped API key.
| Field | Type |
|---|---|
id | UUID |
name | str |
key_prefix | str |
key_type | "rk" | "ak" | "dk" | "pk" |
scopes | list[str] |
scope_version | int |
cidr_allowlist | list[str] | None |
rate_limit_rpm | int | None |
effective_rate_limit_rpm | int | None |
rate_limit_source | "key" | "organization" | "platform_default" | None |
expires_at | datetime | None |
deprecated_at | datetime | None |
revoked_at | datetime | None |
parent_key_id | UUID | None |
created_at | datetime |
last_used_at | datetime | None |
Derived property: status — "active" \| "rotated" \| "revoked".
rate_limit_rpm is the per-key override chosen at mint time; None means no override, not unlimited. effective_rate_limit_rpm is the requests-per-minute ceiling actually enforced for the key once the organization ceiling and the platform default are applied, and rate_limit_source names which of those produced it. Throttling decisions should read effective_rate_limit_rpm. Both are optional on parse: a backend that does not report them leaves each None, which means not reported — never unlimited. A live backend always sends both.
MintedKey
Section titled “MintedKey”Extends APIKeyInfo. Adds:
| Field | Type | Description |
|---|---|---|
api_key | str | Plaintext. Shown ONCE. |
ScopeCatalog
Section titled “ScopeCatalog”| Field | Type |
|---|---|
scope_version | int |
resources | dict[str, ResourceScopes] |
action_verbs | list[str] |
deprecated | list[str] |
ResourceScopes
Section titled “ResourceScopes”| Field | Type |
|---|---|
verbs | list[str] |
Constraints
Section titled “Constraints”RequestRule
Section titled “RequestRule”A TypedDict passed to with_constraints(rule=...) — cryptographically bound
to every request the constrained client makes and enforced on credential-using
calls.
| Field | Type |
|---|---|
rule_type | str |
rule_body | dict |
Content rule authoring types
Section titled “Content rule authoring types”content_match_rule() uses these exported type aliases:
ContentMatchEffect = Literal["deny", "redact", "step_up"]ContentMatchFamily = Literal["send", "read", "write", "delete", "admin", "payment"]ContentParamOp = Literal[ "equals", "any_in", "not_subset_of", "gt", "gte", "lt", "lte",]ContentParamCondition is a TypedDict:
| Field | Type |
|---|---|
name | str |
op | ContentParamOp |
value | str | int | float | list[str] |
Numeric operands must be finite. any_in and not_subset_of take a non-empty
list of pattern strings. See content_match_rule()
for authoring and validation behavior.
Approvals
Section titled “Approvals”PendingApproval
Section titled “PendingApproval”Returned by proxy_request() when an HITL grant requires approval.
| Field | Type | Description |
|---|---|---|
approval_id | UUID | Approval row id. |
approval_group_id | UUID | Shared execution unit for all gates. |
status | "pending" | Always "pending" on creation. |
expires_at | datetime | When the approval window closes. |
expires_in | int | Seconds until expires_at. |
approval_url | str | Deep link to the approver’s wallet UI. Primary delivery — surface this in the agent’s UI. |
gates | list[ApprovalGate] | Every approver gate; each must approve before execution. |
ApprovalGate
Section titled “ApprovalGate”One approver gate in a pending N-of-N approval group.
| Field | Type |
|---|---|
approval_id | UUID |
expires_at | datetime |
expires_in | int |
approval_url | str |
ApprovalStatus
Section titled “ApprovalStatus”Snapshot of an approval row.
| Field | Type | Description |
|---|---|---|
approval_id | UUID | |
status | ApprovalStatusValue | One of the seven status values (see Connect & Grants). |
approval_group_id | UUID | Shared execution unit for the gate group. |
gates | list[ApprovalGateStatus] | Per-gate status breakdown. |
expires_at | datetime | |
decided_at | datetime | None | |
decision_reason | str | None | |
executed_at | datetime | None | |
execution_error | str | None | Operator-safe execution failure when status="failed". |
has_result | bool | True when the proxied result is ready to fetch. |
Derived property: is_terminal — True for denied, expired, executed, failed.
ApprovalGateStatus
Section titled “ApprovalGateStatus”| Field | Type |
|---|---|
approval_id | UUID |
status | ApprovalStatusValue |
expires_at | datetime |
decided_at | datetime | None |
ApprovalStatusValue
Section titled “ApprovalStatusValue”ApprovalStatusValue = Literal[ "pending", "approved", "executing", "denied", "expired", "executed", "failed",]ApprovalResult
Section titled “ApprovalResult”Provider response captured during backend-proxy execution. The body is base64-encoded.
| Field | Type | Description |
|---|---|---|
approval_id | UUID | None | None for synchronous (non-HITL) proxy_request responses. |
status_code | int | HTTP status from the provider. |
headers | dict[str, str] | Provider response headers. |
body_b64 | str | Base64-encoded response body. Validated at construction. |
body_truncated | bool | |
duration_ms | int | None | Provider round-trip duration when available. |
credential_hint | str | None | Why the provider rejected the credential, when the cause is the API surface rather than the credential. Only ever set on 401/403. |
credential_hint addresses a specific dead end. Some providers run several
APIs that accept the same credential but expect it in different headers, so a
perfectly valid key is rejected with 401 purely because the request went to
the other API. The provider’s own error blames the key, and the natural
response — rotating it — changes nothing. When the rejection matches that
pattern, credential_hint says so.
It is None on success and on any rejection that does not match, and it never
changes status_code or the body: the provider’s response is passed through
unchanged and the hint accompanies it. A blank hint is normalized to None, so
if result.credential_hint: is the correct test for “is there an explanation”.
The SDK does not reject a hint that arrives on an unexpected status — the
status rule is the backend’s to enforce, and re-checking it client-side would
turn a future backend addition into a hard failure in already-installed SDKs.
Methods:
body_bytes() -> bytes— decode to raw bytes.body_text(encoding: str = "utf-8") -> str— decode to text.body_json() -> Any— decode and parse JSON.
APICallAuditLog
Section titled “APICallAuditLog”Audit row built for a provider call.
| Field | Type |
|---|---|
grant_id | UUID |
provider_id | str |
method | str |
url | str |
request_headers | dict[str, str] | None |
request_body | Any | None |
response_status | int |
response_headers | dict[str, str] | None |
response_body | Any | None |
latency_ms | int |
timestamp | datetime |
reason | str | None |
context | dict[str, str] | None |
Method sanitize() -> dict[str, Any] strips sensitive headers (Authorization, Cookie, x-api-key, x-amz-*, etc.).
User-defined spans
Section titled “User-defined spans”UserSpan
Section titled “UserSpan”| Field | Type | Description |
|---|---|---|
trace_id | str | Required trace identifier. |
name | str | Required span name. |
start_time | datetime | Required start time. |
span_id | str | None | Stable id for idempotent retries. |
parent_span_id | str | None | Parent span id. |
end_time | datetime | None | End time. |
attributes | dict[str, str] | Caller-defined string attributes. |
EmitSpansResult
Section titled “EmitSpansResult”| Field | Type | Description |
|---|---|---|
accepted | int | Number of newly stored spans; idempotent duplicates are not recounted. |
See spans.emit() for batch limits.
Identity
Section titled “Identity”IdentityContext
Section titled “IdentityContext”Returned by App.resolve_identity() and Agent.resolve_identity(). The canonical identity set the platform resolved for the request — key external data (memory partitions, channel sessions, authorization tuples) on these IDs, never on email or a raw IDP sub.
| Field | Type | Description |
|---|---|---|
app_id | str | The calling application (tenant). |
app_user_id | str | None | Canonical end-user key. None for headless calls. |
external_subject_id | str | None | Stable external join key — the IDP subject identifier. |
idp_id | str | None | Identity provider the subject belongs to. |
user_status | str | None | "active" | "suspended". Deprovisioned users are rejected at resolution, never returned. |
group_ids | list[str] | Stable external group IDs of the user’s unrevoked memberships. |
agent_id | str | None | Canonical agent key. None for plain app callers. |
actor_id | str | None | Audit correlation label only — NOT a partition key. |
trace | IdentityTrace | None | Echo of the ambient run/thread trace context. |
email | str | None | Only with include_profile=True. |
display_name | str | None | Only with include_profile=True. |
Method memory_scope() -> MemoryScope derives the deterministic memory partition keys. See Propagate identity into memory layers.
IdentityTrace
Section titled “IdentityTrace”| Field | Type |
|---|---|
run_id | str | None |
thread_id | str | None |
MemoryScope
Section titled “MemoryScope”Deterministic, prefixed partition keys derived from an IdentityContext.
| Field | Type | Description |
|---|---|---|
user_key | str | None | "alter:user:<app_user_id>"; None for headless contexts. |
agent_key | str | None | "alter:agent:<agent_id>"; None for app callers. |
app_key | str | "alter:app:<app_id>". |
run_key | str | None | "alter:run:<run_id>" from the trace context. |
namespace | tuple[str, ...] | ("alter", <app_id>, <app_user_id>); ("alter", <app_id>) headless. |
IdentityAssertion
Section titled “IdentityAssertion”Returned by App.assert_identity() and Agent.assert_identity().
| Field | Type | Description |
|---|---|---|
token | str | Compact ES256 JWS — verify against the published Alter identity JWKS. Redacted from repr(). |
expires_at | datetime | Absolute expiry (short TTL by design — default 120s, bounds 10–300s). |
identity | IdentityContext | The identity the assertion encodes. |
The assertion is identity-only: it carries no provider tokens, no credentials, no raw IDP claims, and grants nothing by itself — downstream systems map it to their own authorization.