Repository navigation
feat: manage project API tokens - #46
Merged
Merged
Conversation
Add `projects.api_tokens` with `list`, `iterate`, `create` and `delete` for the API tokens of a project (team token), on the sync and async clients. `create` returns the token value once, with the SMTP settings when SMTP is enabled for the project. The surface test lists the team API token operations as pending; the next PR in this stack adds their methods.
bjornftw
approved these changes
Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Adds
projects.api_tokens, a sub-client for the API tokens of a project, in the same style asprojects.report_forwardingandroutes(team token), on bothLettermintandAsyncLettermint:list(project_id, query=None, *, timeout=None)GET /projects/{projectId}/api-tokens({"page": {"size", "cursor"}, "sort": [...]})ListProjectApiTokensResponse(CursorPage[ProjectApiToken])iterate(project_id, query=None, *, timeout=None)next_cursorIterator[ProjectApiToken](AsyncIteratoron the async client)create(project_id, body, *, timeout=None)POST /projects/{projectId}/api-tokenswithStoreProjectApiTokenDataProjectApiTokenCreatedResponse(201)delete(project_id, token_id, *, timeout=None)DELETE /projects/{projectId}/api-tokens/{tokenId}MessageResponsecreatereturns the token value once, indata["token"]. It works for sending through the API, and as the SMTP password when SMTP is enabled for the project;smtpthen holds the SMTP host, ports and username, otherwiseNone."short_token": Truecreates a shorter token for devices that limit password length. The docstring and README say to store the value right away.listanditeratereturn token metadata only.deletestops the token from working immediately.data["token"]is documented as returned once rather than treated specially (same asapi_tokenonprojects.createand the new secret ofwebhooks.regenerate_secret).NotFoundError, a 422 the existingValidationError. No new exception classes...project and token IDs are rejected before any request, as for the other methods.Idempotency-Key, so there is noidempotency_keyargument.ProjectApiTokensandAsyncProjectApiTokensare exported, likeReportForwarding.rotate_tokendocstring now points atapi_tokensfor managing specific tokens.projects.api_tokensis in the Team API table, with a short section and example.Team API token management is added by the PR stacked on top of this one. Until then, the surface test lists those three operations as pending. Every other operation is still checked strictly.
Non-breaking. Suggested bump: minor.
API changes in the regenerated types
GET /projects/{projectId}/api-tokens(cursor paginated,page[size],page[cursor],sort)ListProjectApiTokensQuery,ListProjectApiTokensQueryPage,ListProjectApiTokensQuerySortItem,ListProjectApiTokensResponse,ProjectApiTokenprojects.api_tokens.list/iterate(this PR)POST /projects/{projectId}/api-tokens(201; also 422)StoreProjectApiTokenData,ProjectApiTokenCreatedResponse,ProjectApiTokenSecret,SmtpCredentialsprojects.api_tokens.create(this PR)DELETE /projects/{projectId}/api-tokens/{tokenId}MessageResponseprojects.api_tokens.delete(this PR)GET /team/api-tokens(cursor paginated,page[size],page[cursor],sort)ListTeamApiTokensQuery,ListTeamApiTokensQueryPage,ListTeamApiTokensQuerySortItem,ListTeamApiTokensResponse,TeamApiTokenteam.api_tokens.list/iterate(next PR)POST /team/api-tokens(201; also 422)StoreTeamApiTokenData,TeamApiTokenCreatedResponse,TeamApiTokenSecretteam.api_tokens.create(next PR)DELETE /team/api-tokens/{tokenId}MessageResponseteam.api_tokens.delete(next PR)ApiTokenSource(Literal["dashboard", "api", "cli", "mcp"] | str) forcreated_viaProjectApiToken,TeamApiTokenand the*SecrettypesPOST /projects/{projectId}/rotate-token(no generated change)projects.rotate_tokenonlyNo existing type loses or changes a field.
Usage
Checks
On this branch:
python -m ruff check src tests scripts: passedpython -m ruff format --check src tests scripts: passedpython -m mypy: no issues in 36 source filespython scripts/unasync.py --check:_syncup to datepython scripts/generate.py --check: generated files up to date with the pinned spec, headers OKpython -m pytest tests/: 320 passed on Python 3.10, 3.11, 3.12, 3.13 and 3.14python -m buildandtwine check --strict dist/*: passed; the wheel installs in a fresh venv and imports (UPGRADE.mdandpy.typedincluded)Not run locally
Nothing; every CI step was run locally.
Suggested release tag
v3.3.0(minor), after this PR and the one stacked on it are merged.mainalready has unreleased changes sincev3.2.0(theinbound.rejectedwebhook event in the types), so this fits in the same minor release.