- Add publishable_client_key param to BaseAPIClient, StackServerApp, AsyncStackServerApp
- Include x-stack-publishable-client-key header when key is provided
- Change ExplicitTokenStore defaults from empty string to None
- Remove empty string fallback in resolve_token_store dict branch
- Test publishable_client_key header included/omitted in _build_headers
- Test ExplicitTokenStore defaults to None without arguments
- Test resolve_token_store dict branch defaults missing keys to None
- Test StackServerApp/AsyncStackServerApp accept publishable_client_key
The Stack Auth API returns null for client_metadata, client_read_only_metadata,
and server_metadata when not set, but the models expected dict. Changed to
dict | None to handle null responses gracefully.
- Add Args, Returns, and Raises sections to all public methods on
StackServerApp and AsyncStackServerApp (~46 methods each)
- Expand class-level docstrings with full constructor and usage examples
- Document return-None-on-404 behavior, ValueError raises, and error types
- Add _resolve_customer_path helper for polymorphic customer identification
- Add list_products, get_item, grant_product, cancel_subscription to both sync and async facades
- Add send_email and get_email_delivery_stats to both sync and async facades
- Export ServerItem, AsyncServerItem, EmailDeliveryInfo from package root
- ServerItem wraps Item with increase/decrease/tryDecrease quantity methods
- AsyncServerItem provides async counterpart for quantity operations
- EmailDeliveryInfo model for delivery statistics (delivered/bounced/complained/total)
- Export new models from models package
- create_oauth_provider links OAuth provider to a user
- list_oauth_providers and get_oauth_provider for provider lookup
- list_connected_accounts as alias for list_oauth_providers
- Sync and async versions on both facade classes with tests
- User API key create/list/revoke on StackServerApp and AsyncStackServerApp
- Team API key create/list/revoke on both facades
- check_api_key validates any key and returns user_id/team_id
- Tests for all sync and async API key methods
- list_contact_channels, create_contact_channel for user channels
- send_verification_code with optional callback URL
- verify_contact_channel to validate codes
- Both sync and async facades
- Tests for list, create, send verification, verify on sync facade
- Tests for all contact channel methods on async facade
- Covers optional fields and callback URL parameter
- grant_permission, revoke_permission for team and project scope
- list_permissions with optional direct filter
- has_permission boolean check, get_permission single lookup
- Both sync and async facades
- Tests for grant, revoke, list, has, get permission on sync facade
- Tests for all permission methods on async facade
- Covers team-scoped and project-level permissions
- Add add_team_member and remove_team_member via /team-memberships
- Add send_team_invitation, list_team_invitations, revoke_team_invitation
- Add list_team_member_profiles and get_team_member_profile
- Add user_id field to TeamMemberProfile for profile filtering
- Both sync and async facades have identical method surfaces
- Tests for add_team_member, remove_team_member
- Tests for send/list/revoke team invitations
- Tests for list_team_member_profiles and get_team_member_profile
- Async equivalents for all membership/invitation/profile operations
- Tests for get_team, list_teams, create_team, update_team, delete_team
- Tests for get_team_by_api_key two-step lookup
- Async equivalents for all team CRUD operations
- Uses respx mocking pattern consistent with existing user tests
- list_sessions(user_id) returns list of ActiveSession for a user
- get_session(session_id, user_id) filters from list_sessions, returns ActiveSession or None
- revoke_session(session_id, user_id) deletes session via DELETE endpoint
- Both sync and async facades implement all three methods
- StackServerApp (sync) and AsyncStackServerApp (async) facade classes
- get_user returns ServerUser or None on 404
- list_users with cursor/limit/order_by/desc/query/include_restricted/include_anonymous
- create_user sends only non-None fields
- update_user uses _UNSET sentinel to distinguish not-provided from explicit None
- delete_user returns None
- get_user_by_api_key performs two-step lookup via /api-keys/check then /users/{id}
- Both classes support context managers and compose API client + optional token store
- Tests for get_user, list_users, create_user, update_user, delete_user
- Tests for get_user_by_api_key two-step lookup
- Tests for sentinel pattern in update_user (None vs not-provided)
- Both sync and async facades covered with respx mocks
- TokenStore ABC with get_stored_access_token, get_stored_refresh_token, compare_and_set
- MemoryTokenStore, ExplicitTokenStore, RequestTokenStore implementations
- Module-level registry shares MemoryTokenStore instances per project_id
- resolve_token_store handles memory/dict/request/None init variants
- CAS refresh algorithm (sync + async) with 20s expiry buffer and 75s iat threshold
- Token refresh via form-encoded POST to /api/v1/auth/oauth/token
- Per-instance threading.Lock and asyncio.Lock for concurrency safety
- 34 passing tests covering all store types, registry, helpers, and CAS branches
- Add AuthState, TokenPartialUser, decode_access_token_claims to public API
- Add sync_authenticate_request and async_authenticate_request exports
- Existing exports unchanged
- TokenPartialUser frozen dataclass for unverified JWT payload extraction
- AuthState frozen dataclass for authentication results
- decode_access_token_claims extracts user info without signature verification
- _extract_token_from_headers handles Authorization and x-stack-auth headers
- sync_authenticate_request and async_authenticate_request compose with JWKS verifier
- PaginatedResult[T] generic wrapping nested {items, pagination: {next_cursor}} shape
- Convenience properties .next_cursor and .has_next_page
- Package __init__.py exports all models, errors, and PaginatedResult
- SyncAPIClient/AsyncAPIClient intentionally excluded from public API
- Tests verify pagination with ServerUser and Team types
- BaseAPIClient generic base with header construction, URL building,
response parsing via x-stack-actual-status and x-stack-known-error
- SyncAPIClient wrapping httpx.Client with context manager support
- AsyncAPIClient wrapping httpx.AsyncClient with async context manager
- Exponential backoff retry for idempotent methods (GET/PUT/DELETE)
- 429 rate limit handling with Retry-After header support
- POST/PUT/PATCH with no body sends {} as JSON
- All 26 tests passing
- BaseUser and ServerUser with camelCase aliases and millis-to-datetime properties
- Team, ServerTeam, TeamMemberProfile, TeamInvitation with timestamp conversions
- ActiveSession with GeoInfo nested model
- ContactChannel and TeamPermission/ProjectPermission
- Tests for user and team model parsing, inheritance, and field conversion
- StackAuthError base with code/message/details and from_response() factory
- 13 category subclasses: Authentication, NotFound, Validation, PermissionDenied,
Conflict, OAuth, Passkey, ApiKey, Payment, Email, RateLimit, Cli, Analytics
- Complete _ERROR_CODE_MAP with 143 error codes from known-errors.tsx
- StackAuthModel base class with Pydantic v2 ConfigDict and millis conversion
- Package exports via __init__.py with __all__
- Test StackAuthError base class storage and string representation
- Test all 13 category subclasses inherit from StackAuthError
- Test from_response() dispatch for known and unknown error codes
- Test StackAuthModel config and millis conversion
- Package skeleton with pyproject.toml (stubs only, tests expected to fail)