Skip to content

feat: manage project API tokens - #47

Merged
bjarn merged 2 commits into
mainfrom
feat/project-api-tokens
Oct 30, 2026
Merged

bjarn merged 2 commits into
mainfrom
feat/project-api-tokens

Conversation

@bjarn

@bjarn bjarn commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Non-breaking. Adds Projects.APITokens, a ProjectAPITokensService that manages Project API tokens, and regenerates the types from the latest API spec. Suggested version bump: minor.

Method Request
Projects.APITokens.List(ctx, projectID, *ListProjectApiTokensQuery, ...RequestOption) (*ListProjectApiTokensResponse, error) GET /projects/{projectId}/api-tokens
Projects.APITokens.Iterate(ctx, projectID, *ListProjectApiTokensQuery, ...RequestOption) iter.Seq2[ProjectApiToken, error] GET /projects/{projectId}/api-tokens, following next_cursor
Projects.APITokens.Create(ctx, projectID, StoreProjectApiTokenData, ...RequestOption) (*ProjectApiTokenCreatedResponse, error) POST /projects/{projectId}/api-tokens
Projects.APITokens.Delete(ctx, projectID, tokenID, ...RequestOption) (*MessageResponse, error) DELETE /projects/{projectId}/api-tokens/{tokenId}
  • All four need a team token and take no Idempotency-Key. Like Team.Invites and Webhooks.Deliveries, the paginated collection is a sub-resource. The service follows them: the same options, empty-ID validation, pagination iterator and error types (422 is a *ValidationError, 404 a *NotFoundError).
  • The token's value is only returned once, in ProjectApiTokenCreatedResponse.Data.Token. Use it to send through the API, or as the SMTP password when SMTP is enabled for the project; SMTP then holds the host, ports and user name, and is nil otherwise. The list returns tokens without their values. The doc comments and README say so.
  • The deprecation note on Projects.RotateToken now points at Projects.APITokens.
  • The regenerated types also contain the Team API token operations. Their methods are added by the PR stacked on top of this one. Until then, the coverage test (TestEveryOperationHasAMethod) lists those three operations as pending. Every other operation is still strictly checked, and the test fails if a pending operation gets a method without being removed from the list.
  • The operation count in the coverage test goes from 71 to 77.
  • README: Projects.APITokens in the Team API table, with an example.

API changes in the regenerated types

Operation or type Change SDK method
GET /projects/{projectId}/api-tokens New: the project's API tokens without their values, cursor paginated (page[size], page[cursor], sort); ListProjectApiTokensQuery, ListProjectApiTokensQuerySortItem, ListProjectApiTokensResponse Projects.APITokens.List, Projects.APITokens.Iterate (this PR)
POST /projects/{projectId}/api-tokens New: create a token; 201 ProjectApiTokenCreatedResponse, 422; body StoreProjectApiTokenData (Name, optional ShortToken) Projects.APITokens.Create (this PR)
DELETE /projects/{projectId}/api-tokens/{tokenId} New: delete a token; MessageResponse Projects.APITokens.Delete (this PR)
GET /team/api-tokens New: the team's API tokens without their values, cursor paginated; ListTeamApiTokensQuery, ListTeamApiTokensQuerySortItem, ListTeamApiTokensResponse Team.APITokens.List, Team.APITokens.Iterate (stacked PR)
POST /team/api-tokens New: create a token; 201 TeamApiTokenCreatedResponse, 422; body StoreTeamApiTokenData (Name, Abilities, optional ExpiresAt) Team.APITokens.Create (stacked PR)
DELETE /team/api-tokens/{tokenId} New: delete a token; MessageResponse Team.APITokens.Delete (stacked PR)
ProjectApiToken, ProjectApiTokenSecret, ProjectApiTokenCreatedResponse, SmtpCredentials New. ProjectApiTokenSecret is ProjectApiToken plus the one-time Token; the created response adds SMTP (nullable) and Message
TeamApiToken, TeamApiTokenSecret, TeamApiTokenCreatedResponse New. TeamApiTokenSecret is TeamApiToken plus the one-time Token
ApiTokenSource New open enum: where a token was created (dashboard, api, cli, mcp)
POST /projects/{projectId}/rotate-token Description only: it now points at the Project API token endpoints. No generated code changes from it

No existing type changes. gorelease reports only compatible changes (see Checks).

Usage

client, err := lettermint.New(lettermint.WithTeamToken(os.Getenv("LETTERMINT_TEAM_TOKEN")))

created, err := client.Projects.APITokens.Create(ctx, "project-id", lettermint.StoreProjectApiTokenData{Name: "Production server"})
if err != nil {
	return err
}
token := created.Data.Token // the token's value, shown once: store it now

for t, err := range client.Projects.APITokens.Iterate(ctx, "project-id", nil) {
	if err != nil {
		return err
	}
	fmt.Println(t.Name, t.CreatedAt)
}

_, err = client.Projects.APITokens.Delete(ctx, "project-id", created.Data.ID)

Checks

Run on this branch with GOFLAGS=-buildvcs=false (worktree):

  • test -z "$(gofmt -l .)": passes
  • go vet ./...: passes
  • sh scripts/generate.sh --check: passes (generated files up to date; the header check that CI runs also passes)
  • go test -race -coverprofile=coverage.out ./...: passes (coverage 94.4%)
  • go build ./...: passes
  • go mod tidy && git diff --exit-code go.mod: no changes (no go.sum)
  • go vet, go test -race and go build with Go 1.24.13 (minimum), 1.26.4 and 1.27.1: pass
  • gorelease -base=v3.2.0: only compatible changes; suggested version v3.3.0. It lists ProjectAPITokensService and ProjectsService.APITokens as added. Besides this PR's additions it lists WebhookEventInboundRejected: added, which is already on main.

Not run locally

  • The Codecov upload.

Suggested release tag

v3.3.0, once this PR and the stacked team API token PR are merged (one minor release for both).

@bjarn
bjarn requested a review from a team as a code owner October 29, 2026 16:53
@bjarn
bjarn added this pull request to stack #49 October 29, 2026 16:53
@bjarn
bjarn force-pushed the feat/project-api-tokens branch from 5fcb273 to 0c1a652 Compare October 29, 2026 22:04
@bjarn
bjarn merged commit 5ed9da6 into main Oct 30, 2026
4 checks passed
@bjarn
bjarn deleted the feat/project-api-tokens branch October 30, 2026 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants