Skip to content

feat: add team invites - #42

Merged
bjarn merged 2 commits into
mainfrom
feat/team-invites
Oct 21, 2026
Merged

bjarn merged 2 commits into
mainfrom
feat/team-invites

Conversation

@bjarn

@bjarn bjarn commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What changed

Non-breaking. Adds Team.Invites, a TeamInvitesService that manages pending team invitations, and regenerates the types from the latest API spec. Suggested version bump: minor.

Method Request
Team.Invites.List(ctx, *ListTeamInvitesQuery, ...RequestOption) (*ListTeamInvitesResponse, error) GET /team/invites
Team.Invites.Iterate(ctx, *ListTeamInvitesQuery, ...RequestOption) iter.Seq2[TeamInviteData, error] GET /team/invites, following next_cursor
Team.Invites.Retrieve(ctx, inviteID, ...RequestOption) (*TeamInviteData, error) GET /team/invites/{inviteId}
Team.Invites.Create(ctx, CreateTeamInviteData, ...RequestOption) (*TeamInviteData, error) POST /team/invites
Team.Invites.Update(ctx, inviteID, UpdateTeamInviteData, ...RequestOption) (*TeamInviteData, error) PUT /team/invites/{inviteId}
Team.Invites.Revoke(ctx, inviteID, ...RequestOption) (*MessageResponse, error) DELETE /team/invites/{inviteId}
  • All six need a team token. They follow Team.Members: the same options, empty-ID validation and error types (409 is a *ConflictError, 422 a *ValidationError).
  • Create sends the invitation email. The team needs a free seat, and the Owner role can't be assigned. Update replaces the email address, role and project access, and sends a new email only when the address changes.
  • The regenerated types also contain the team role and member removal operations. Their methods are added by the PRs stacked on top of this one. Until then, the coverage test (TestEveryOperationHasAMethod) lists those six 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 60 to 71.
  • README: Team.Invites in the Team API table, with an example.

API changes in the regenerated types

Operation or type Change SDK method
GET /team/invites New: pending invitations, cursor paginated (page[size], page[cursor]); ListTeamInvitesQuery, ListTeamInvitesResponse Team.Invites.List, Team.Invites.Iterate (this PR)
GET /team/invites/{inviteId} New: one pending invitation (TeamInviteData) Team.Invites.Retrieve (this PR)
POST /team/invites New: invite a person and send the invitation email; 201 TeamInviteData, 409, 422; body CreateTeamInviteData Team.Invites.Create (this PR)
PUT /team/invites/{inviteId} New: replace the email, role and project access; 409, 422; body UpdateTeamInviteData Team.Invites.Update (this PR)
DELETE /team/invites/{inviteId} New: revoke a pending invitation; MessageResponse Team.Invites.Revoke (this PR)
GET /team/roles/{roleId} New: one role (TeamRoleData) Team.RetrieveRole (stacked PR)
POST /team/roles New: create a custom role; 201 TeamRoleData, 409, 422; body CreateTeamRoleData Team.CreateRole (stacked PR)
PUT /team/roles/{roleId} New: replace a custom role's name, description and permissions; 409, 422; body UpdateTeamRoleData Team.UpdateRole (stacked PR)
DELETE /team/roles/{roleId} New: delete a custom role; optional replacement_role_id query (DeleteTeamRoleQuery); MessageResponse, 409, 422 Team.DeleteRole (stacked PR)
POST /team/roles/{roleId}/duplicate New: copy a role under a new name; 201 TeamRoleData; body DuplicateTeamRoleData Team.DuplicateRole (stacked PR)
DELETE /team/members/{userId} New: remove a non-Owner member; MessageResponse, 409 Team.Members.Remove (stacked PR)
GET /team/members New optional filter[project_id] query parameter (ListTeamMembersQuery.FilterProjectID): only members who can access the project Team.Members.List, Team.Members.Iterate already take the query (stacked PR adds the test and docs)
TeamRoleData New Description field (Nullable[string], optional)
TeamInviteData, TeamInviteDataRole New: {id, email, role {id, name}, project_access, created_at}

Usage

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

invite, err := client.Team.Invites.Create(ctx, lettermint.CreateTeamInviteData{
	Email:  "jane@example.com",
	RoleID: "role-id",
	ProjectAccess: lettermint.CreateTeamInviteDataProjectAccess{
		Scope:      lettermint.ProjectAccessScopeSelected,
		ProjectIDs: []string{"project-id"},
	},
})

for invite, err := range client.Team.Invites.Iterate(ctx, nil) {
	if err != nil {
		return err
	}
	fmt.Println(invite.Email, invite.Role.Name)
}

_, err = client.Team.Invites.Revoke(ctx, invite.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.3%)
  • 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.1.0 (top of the stack): every change from this stack is compatible. It reports one incompatible change, SendBatchMailRequest: changed from []SendMailRequest to []SendBatchMailItem, which is already on main from the per-email idempotency keys change (feat: add per-email idempotency keys to batch send #38) and not part of this stack. Against current main (gorelease -base=github.com/lettermint/lettermint-go/v3@8982ddb), it reports only compatible changes, including TeamRoleData.Description: added, ListTeamMembersQuery.FilterProjectID: added and TeamService.Invites: added.

Not run locally

  • The Codecov upload.

Suggested release tag

v3.2.0 (minor: new methods and fields), after the whole stack is merged.

🤖 Generated with Claude Code

bjarn and others added 2 commits October 20, 2026 22:38
- Team invites: list (cursor paginated), get, create, update and revoke
  (/team/invites), with TeamInviteData, CreateTeamInviteData,
  UpdateTeamInviteData and ListTeamInvitesQuery.
- Team roles: get, create, update, delete (optional replacement_role_id
  query, DeleteTeamRoleQuery) and duplicate (/team/roles), with
  CreateTeamRoleData, UpdateTeamRoleData and DuplicateTeamRoleData.
- Team members: remove a member (DELETE /team/members/{userId}).
- GET /team/members takes the optional filter[project_id] query parameter
  (ListTeamMembersQuery.FilterProjectID).
- TeamRoleData gains the optional, nullable description field.

Co-Authored-By: Claude <noreply@anthropic.com>
Add Team.Invites (TeamInvitesService) to list, iterate, retrieve, create,
update and revoke pending team invitations. Needs a team token.

The team role and member removal operations are listed as pending in the
coverage test; the next changes add their methods.

Co-Authored-By: Claude <noreply@anthropic.com>
@bjarn
bjarn requested a review from a team as a code owner October 20, 2026 20:45
@bjarn
bjarn added this pull request to stack #45 October 20, 2026 20:46
@bjarn
bjarn merged commit e44638b into main Oct 21, 2026
4 checks passed
@bjarn
bjarn deleted the feat/team-invites branch October 21, 2026 14:04
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