Skip to content

feat: add team invites - #67

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. Suggested version: 3.2.0 (minor).

  • Regenerates co.lettermint.types from the latest API spec (separate commit, generated files only).
  • Adds team().invites() for pending team invitations: list, iterate, retrieve, create, update and revoke. Each method has the usual overloads with and without ListTeamInvitesQuery and RequestOptions, validates the invite ID before sending, and uses the team token.
  • The role management and member removal operations are added by the PRs stacked on top of this one. Until then, the operation coverage test in TeamApiTest lists those six operations as pending; everything else stays strictly checked.

Generated records that tools may flag (a minor change, as in earlier minors):

  • TeamRoleData: the canonical record constructor gains a description argument.
  • ListTeamMembersQuery: the canonical record constructor gains a filterProjectId argument. The builders are unchanged for existing callers.

API changes in the regenerated types

Operation / type Change SDK method
GET /team/invites New: pending invitations, cursor paginated (page[size], page[cursor]) team().invites().list / iterate (this PR)
GET /team/invites/{inviteId} New: retrieve an invitation team().invites().retrieve (this PR)
POST /team/invites New: invite a person and send the invitation email (201). Needs a free seat; the Owner role can't be assigned. 409, 422 team().invites().create (this PR)
PUT /team/invites/{inviteId} New: replace email, role and project access; a new email is sent only when the address changes. 409, 422 team().invites().update (this PR)
DELETE /team/invites/{inviteId} New: revoke a pending invitation team().invites().revoke (this PR)
GET /team/roles/{roleId} New: retrieve a role team().retrieveRole (stacked PR)
POST /team/roles New: create a custom role (201). 409, 422 team().createRole (stacked PR)
PUT /team/roles/{roleId} New: replace a custom role's name, description and permissions. 409, 422 team().updateRole (stacked PR)
DELETE /team/roles/{roleId} New: delete a custom role, with the optional replacement_role_id query. 409, 422 team().deleteRole (stacked PR)
POST /team/roles/{roleId}/duplicate New: copy a role under a new name (201) team().duplicateRole (stacked PR)
DELETE /team/members/{userId} New: remove a non-Owner member. 409 team().members().remove (stacked PR)
GET /team/members New optional filter[project_id] query (ListTeamMembersQuery.filterProjectId) existing team().members().list / iterate
TeamRoleData New description (optional, nullable string) existing team().roles() and the new role methods
New types TeamInviteData, TeamInviteDataRole, CreateTeamInviteData, CreateTeamInviteDataProjectAccess, UpdateTeamInviteData, UpdateTeamInviteDataProjectAccess, ListTeamInvitesQuery, CreateTeamRoleData, UpdateTeamRoleData, DuplicateTeamRoleData, DeleteTeamRoleQuery

The 409 responses of the new operations carry an error code (stale_resource, owner_protected, last_owner, built_in_role_immutable, custom_role_requires_pro); they map to the existing ConflictException, and 422 responses to ValidationException. The other generated files only change their spec header.

Usage

TeamInviteData invite = lettermint.team().invites().create(new CreateTeamInviteData("ada@acme.com", roleId,
    new CreateTeamInviteDataProjectAccess(ProjectAccessScope.of("selected"), List.of(projectId))));

for (TeamInviteData pending : lettermint.team().invites().iterate()) {
    System.out.println(pending.email() + " " + pending.role().name());
}

lettermint.team().invites().revoke(invite.id());

Checks

  • ./gradlew generateTypes with the latest spec, then ./gradlew checkGeneratedTypes: up to date (217 files)
  • ./gradlew clean build --no-daemon on JDK 17: passed (97 tests, 0 failures)
  • ./gradlew clean build --no-daemon on JDK 24: passed (97 tests, 0 failures)
  • CI build-output checks on the jar: Automatic-Module-Name: co.lettermint present, no okhttp bundled

Not run locally

  • JDK 21 and 25 (CI matrix); not installed locally. JDK 17 and 24 passed.
  • Windows (CI matrix).
  • ./gradlew checkstyleMain checkstyleTest: the build has no checkstyle tasks, and the workflow ignores the result (|| true).

Suggested release tag

v3.2.0, after the whole stack has merged.

🤖 Generated with Claude Code

bjarn and others added 2 commits October 20, 2026 22:38
- Team invites: list (cursor paginated), retrieve, create, update and revoke
  (`/team/invites`, `/team/invites/{inviteId}`)
- Team roles: retrieve, create, update, delete (optional
  `replacement_role_id` query) and duplicate (`/team/roles/{roleId}`,
  `/team/roles`, `/team/roles/{roleId}/duplicate`)
- Team members: remove a member (`DELETE /team/members/{userId}`)
- `GET /team/members` takes the optional `filter[project_id]` query
- `TeamRoleData` gains an optional, nullable `description`

Co-Authored-By: Claude <noreply@anthropic.com>
Adds `team().invites()` with `list`, `iterate`, `retrieve`, `create`,
`update` and `revoke` for pending team invitations.

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