organization

package
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 25 Imported by: 0

README

organization

Multi-tenancy: organisations, members, roles, invitations and teams.

Without this plugin registered, none of that exists. The core engine knows about users and sessions and stops there. Add this and you get the whole B2B shape, where a person belongs to one or more organisations, holds a role in each, and can be invited into one by email.

Teams sit inside organisations for finer-grained grouping, and the plugin contributes its own dashboard pages and admin routes.

Use it when

  • You are selling to companies, so the account that pays you is not the person who logs in
  • Permissions need to be scoped per tenant, where the same user is an admin in one org and a viewer in another
  • You need invitation flows with accept and decline

Skip it when

  • Every user is an island. Consumer products rarely need this and it complicates every query you write
  • You have exactly one tenant, which is most internal tools
  • You want organisations later. Adding them after launch means backfilling ownership onto everything you already store

Wiring

authsome.WithPlugin(organization.New()),

Mount the routes somewhere other than the engine base path:

authsome.WithPlugin(organization.New(organization.Config{
    PathPrefix: "/v1/tenants",
})),

Config

Field Type Default What it does
PathPrefix string engine BasePath Where organisation routes are mounted
InvitationTTL time.Duration 7 days How long an invitation can be accepted. Accepting a lapsed one answers 410. Set it negative and invitations never expire

Endpoints

Method Path Purpose
GET /orgs List the caller's organisations
POST /orgs Create an organisation
GET /orgs/check-slug Check slug availability before creating
GET /orgs/:orgId Read an organisation
PATCH /orgs/:orgId Update an organisation
DELETE /orgs/:orgId Delete an organisation
GET /orgs/:orgId/members List members
POST /orgs/:orgId/members Add a member
PATCH /orgs/:orgId/members/:memberId Change a member's role
DELETE /orgs/:orgId/members/:memberId Remove a member
GET /orgs/:orgId/invitations List pending invitations
POST /orgs/:orgId/invitations Invite someone by email
POST /orgs/invitations/accept Accept an invitation
POST /orgs/invitations/decline Decline an invitation
GET /orgs/:orgId/teams List teams
POST /orgs/:orgId/teams Create a team
GET /orgs/:orgId/teams/:teamId Read a team
PATCH /orgs/:orgId/teams/:teamId Update a team
DELETE /orgs/:orgId/teams/:teamId Delete a team

An admin variant of the list endpoint is mounted under {PathPrefix}/admin.

Lifecycle hooks

None on the sign-in path. It registers a DataExportContributor, so a user's organisation memberships land in their GDPR export.

sso and scim scope their connections to an organisation. subscription can bill per organisation and auto-subscribe one on creation. consent covers the other half of the GDPR story.

Documentation

Overview

contract.go: Wires the organization plugin into the forge-dashboard contract surface via plugin.ContractContributor.

Package organization provides an opt-in organization management plugin for AuthSome.

When registered, this plugin contributes organization CRUD, member management, invitation workflows, team management, dashboard pages, and admin routes. Without this plugin, no organization functionality exists.

Index

Constants

View Source
const DefaultInvitationTTL = 7 * 24 * time.Hour

DefaultInvitationTTL is how long an invitation stays acceptable when Config.InvitationTTL is left at zero.

View Source
const ResendInvitationInterval = time.Minute

ResendInvitationInterval is how long an invitation must wait between sends.

Variables

View Source
var ErrInvitationExpired = errors.New("organization: invitation has expired")

ErrInvitationExpired is returned when an invitation is accepted after its ExpiresAt. The HTTP layer answers it with 410 Gone.

View Source
var ErrInvitationNotActionable = errors.New("organization: invitation can no longer be changed")

ErrInvitationNotActionable is returned by ResendInvitation and RevokeInvitation when the invitation has already been accepted, declined or revoked. The HTTP layer answers it with 409 Conflict.

View Source
var ErrInvitationNotPending = errors.New("organization: invitation is not pending")

ErrInvitationNotPending is returned when an invitation is accepted or declined after it was already accepted, declined or revoked. The HTTP handlers answer it with 409.

View Source
var ErrInvitationResendTooSoon = errors.New("organization: invitation was sent a moment ago")

ErrInvitationResendTooSoon is returned by ResendInvitation inside ResendInvitationInterval of the last send. The HTTP layer answers it with 429 Too Many Requests.

View Source
var ErrSlugTaken = errors.New("organization: slug already taken")

ErrSlugTaken is returned when an organization is created with a slug that another organization in the same app already holds. The HTTP layer answers it with 409 Conflict.

Functions

This section is empty.

Types

type AcceptInvitationRequest added in v0.0.7

type AcceptInvitationRequest struct {
	Token string `json:"token" description:"Invitation token"`
}

AcceptInvitationRequest binds the body for POST /orgs/invitations/accept.

type AddMemberRequest added in v1.9.0

type AddMemberRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	UserID string `json:"user_id" description:"User identifier to add"`
	Role   string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}

AddMemberRequest binds path + body for POST /orgs/:orgId/members.

type AdminListOrgsRequest added in v1.9.0

type AdminListOrgsRequest struct {
	AppID string `query:"app_id" description:"Application ID"`
}

AdminListOrgsRequest binds query params for GET /admin/orgs.

type CheckSlugRequest added in v1.9.0

type CheckSlugRequest struct {
	AppID string `query:"app_id" description:"Application ID"`
	Slug  string `query:"slug" description:"Slug to check"`
}

CheckSlugRequest binds query params for GET /orgs/check-slug.

type Config

type Config struct {
	// PathPrefix is the HTTP path prefix for organization routes.
	// Defaults to the engine's BasePath.
	PathPrefix string

	// InvitationTTL is how long an invitation can be accepted after it is
	// created. Zero means DefaultInvitationTTL (seven days). A negative value
	// turns expiry off, so invitations never lapse, which was the behaviour
	// before this option existed.
	InvitationTTL time.Duration

	// InvitationURLTemplate is the link an invitation email carries, with
	// {token} standing in for the invitation token, for example
	// "https://app.example.com/accept-invite?token={token}". Empty (the
	// default) means the plugin sends no invitation email, which was the
	// behaviour before this option existed. Mail goes through the plugin
	// registered as "notification"; without one nothing is sent either.
	InvitationURLTemplate string

	// RoleRanks gives role names other than owner, admin and member a rank
	// for the checks that compare roles: who may read or change an
	// organization, grant a role, or remove a member. Keys are lower case
	// names; values are on the built-in scale, member 1, admin 2, owner 3,
	// so {"builder": 1} lets a builder do what a member can and nothing
	// more. A name not listed ranks 0, below member, which is how every
	// custom role was treated before this option existed. The built-in names
	// cannot be changed here. Be careful with 2 or 3: a role ranked as admin
	// passes every admin-level check in this plugin.
	RoleRanks map[string]int
}

Config configures the organization plugin.

type CreateInvitationRequest added in v1.9.0

type CreateInvitationRequest struct {
	OrgID string `path:"orgId" description:"Organization identifier"`
	Email string `json:"email" description:"Email address to invite"`
	Role  string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}

CreateInvitationRequest binds path + body for POST /orgs/:orgId/invitations.

type CreateOrgRequest added in v1.9.0

type CreateOrgRequest struct {
	AppID string `json:"app_id,omitempty" description:"Application ID (optional, uses default)"`
	Name  string `json:"name" description:"Organization name"`
	Slug  string `json:"slug" description:"URL-safe slug"`
}

CreateOrgRequest binds the body for POST /orgs.

type CreateTeamRequest

type CreateTeamRequest struct {
	OrgID string `path:"orgId" description:"Organization identifier"`
	Name  string `json:"name" description:"Team name"`
	Slug  string `json:"slug" description:"URL-safe team slug"`
}

CreateTeamRequest binds path + body for POST /orgs/:orgId/teams.

type DeclineInvitationRequest added in v0.0.7

type DeclineInvitationRequest struct {
	Token string `json:"token" description:"Invitation token"`
}

DeclineInvitationRequest binds the body for POST /orgs/invitations/decline.

type DeleteOrgRequest added in v1.9.0

type DeleteOrgRequest struct {
	OrgID string `path:"orgId" description:"Organization identifier"`
}

DeleteOrgRequest binds the path for DELETE /orgs/:orgId.

type DeleteTeamRequest added in v0.0.7

type DeleteTeamRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	TeamID string `path:"teamId" description:"Team identifier"`
}

DeleteTeamRequest binds the path for DELETE /orgs/:orgId/teams/:teamId.

type GetInvitationRequest added in v1.10.0

type GetInvitationRequest struct {
	Token string `path:"inviteToken" description:"Invitation token"`
}

GetInvitationRequest binds GET /orgs/invitations/:token.

type GetOrgRequest added in v1.9.0

type GetOrgRequest struct {
	OrgID string `path:"orgId" description:"Organization identifier"`
}

GetOrgRequest binds the path for GET /orgs/:orgId.

type GetTeamRequest added in v1.9.0

type GetTeamRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	TeamID string `path:"teamId" description:"Team identifier"`
}

GetTeamRequest binds the path for GET /orgs/:orgId/teams/:teamId.

type InvitationListResponse added in v1.9.0

type InvitationListResponse struct {
	Invitations any `json:"invitations" description:"List of invitations"`
	// NextCursor is set when more remain; pass it as cursor to continue.
	NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}

InvitationListResponse wraps a list of invitations.

type InvitationPreview added in v1.10.0

type InvitationPreview struct {
	OrgID       string    `json:"org_id" description:"Organization the invitation is for"`
	OrgName     string    `json:"org_name" description:"Organization's display name"`
	OrgSlug     string    `json:"org_slug,omitempty" description:"Organization's slug"`
	InviterName string    `json:"inviter_name" description:"Who sent the invitation"`
	Role        string    `json:"role" description:"Role the invitee will hold"`
	EmailMasked string    `json:"email_masked" description:"Partially masked invited address"`
	ExpiresAt   time.Time `json:"expires_at" description:"When the invitation stops working"`
}

InvitationPreview is what the person holding an invitation token is shown before they accept it.

Deliberately not organization.Invitation. That row has no organization name and no inviter name, which are the two things the page actually needs, and it carries the token and the raw invited address, which the page does not. The email is masked so the screen can say who the invitation is for without handing the address to whoever opened the link.

type ListInvitationsRequest added in v1.9.0

type ListInvitationsRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	Limit  int    `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
	Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}

ListInvitationsRequest binds the path for GET /orgs/:orgId/invitations.

type ListMembersRequest added in v0.0.7

type ListMembersRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	Limit  int    `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
	Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}

ListMembersRequest binds the path for GET /orgs/:orgId/members.

type ListOrgsRequest added in v1.9.0

type ListOrgsRequest struct {
	Limit  int    `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
	Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}

ListOrgsRequest is an empty request for GET /orgs (user from context).

type ListTeamsRequest added in v0.0.7

type ListTeamsRequest struct {
	OrgID  string `path:"orgId" description:"Organization identifier"`
	Limit  int    `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
	Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}

ListTeamsRequest binds the path for GET /orgs/:orgId/teams.

type MemberListResponse added in v1.9.0

type MemberListResponse struct {
	Members any `json:"members" description:"List of members"`
	// NextCursor is set when more remain; pass it as cursor to continue.
	NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}

MemberListResponse wraps a list of members.

type OrgListResponse added in v1.9.0

type OrgListResponse struct {
	Organizations any `json:"organizations" description:"List of organizations"`
	// NextCursor is set when more remain; pass it as cursor to continue.
	NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}

OrgListResponse wraps a list of organizations.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is the organization management plugin.

func New added in v1.9.0

func New(cfg ...Config) *Plugin

New creates a new organization plugin with optional configuration. The store is resolved automatically from the engine during OnInit.

func (*Plugin) AcceptInvitation added in v1.9.0

func (p *Plugin) AcceptInvitation(ctx context.Context, token string) (*organization.Member, error)

AcceptInvitation accepts a pending invitation by token and adds whichever account owns the invited email (primary or secondary) as a member. It does not check who is asking, so call it only from code you trust. The HTTP endpoint uses AcceptInvitationAs instead.

func (*Plugin) AcceptInvitationAs added in v1.9.0

func (p *Plugin) AcceptInvitationAs(ctx context.Context, token string, userID id.UserID) (*organization.Member, error)

AcceptInvitationAs accepts a pending invitation on behalf of userID, the signed-in caller. It succeeds only when userID owns the invited email, primary or secondary. Anyone else gets store.ErrNotFound, exactly what an unknown token gets, and the invitation is left untouched.

func (*Plugin) AddMember added in v1.9.0

func (p *Plugin) AddMember(ctx context.Context, m *organization.Member) error

AddMember adds a member to an organization.

func (*Plugin) AdminListOrganizations added in v1.9.0

func (p *Plugin) AdminListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)

AdminListOrganizations returns all organizations for the given app.

func (*Plugin) CreateInvitation added in v1.9.0

func (p *Plugin) CreateInvitation(ctx context.Context, inv *organization.Invitation) error

CreateInvitation creates an organization invitation. An invitation with no ExpiresAt gets one from Config.InvitationTTL (seven days by default).

func (*Plugin) CreateOrganization added in v1.9.0

func (p *Plugin) CreateOrganization(ctx context.Context, o *organization.Organization) error

CreateOrganization creates a new organization and adds the creator as owner.

func (*Plugin) CreateTeam added in v1.9.0

func (p *Plugin) CreateTeam(ctx context.Context, t *organization.Team) error

CreateTeam creates a new team within an organization.

func (*Plugin) DeclineInvitation added in v1.9.0

func (p *Plugin) DeclineInvitation(ctx context.Context, token string) error

DeclineInvitation declines a pending invitation by token. It does not check who is asking, so call it only from code you trust. The HTTP endpoint uses DeclineInvitationAs instead.

func (*Plugin) DeclineInvitationAs added in v1.11.0

func (p *Plugin) DeclineInvitationAs(ctx context.Context, token string, userID id.UserID) error

DeclineInvitationAs declines an invitation on behalf of userID, the signed-in caller, under the same rule as AcceptInvitationAs: only the invitee may, and anyone else gets store.ErrNotFound with nothing changed.

func (*Plugin) DeleteOrganization added in v1.9.0

func (p *Plugin) DeleteOrganization(ctx context.Context, orgID id.OrgID) error

DeleteOrganization deletes an organization and all of its dependent records (members, teams, invitations) before emitting the AfterOrgDelete hook so other plugins (subscription, SCIM, …) can clean up their own org-scoped data.

The cascade is delegated to Store.DeleteOrganizationCascade, which each backend implements with its native transaction primitive (PgTx / SqliteTx / MongoTx) so a midway failure rolls back atomically. EmitAfterOrgDelete only fires once the cascade returns nil, so downstream plugins never see an event for an org that wasn't actually deleted.

Once the org is gone, every session its members hold that names it as the active org has that org cleared, the same as RemoveMember does for one member. A failure there is logged, not returned.

func (*Plugin) DeleteTeam added in v1.9.0

func (p *Plugin) DeleteTeam(ctx context.Context, teamID id.TeamID) error

DeleteTeam deletes a team.

func (*Plugin) ExportUserData added in v1.9.0

func (p *Plugin) ExportUserData(ctx context.Context, userID id.UserID) (label string, data any, err error)

ExportUserData returns the user's organization data for GDPR export.

func (*Plugin) GetInvitation added in v1.11.0

func (p *Plugin) GetInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)

GetInvitation returns an invitation of orgID by ID. An invitation that belongs to another organization is store.ErrNotFound, the same as one that does not exist.

func (*Plugin) GetOrganization added in v1.9.0

func (p *Plugin) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)

GetOrganization returns an organization by ID.

func (*Plugin) GetTeam added in v1.9.0

func (p *Plugin) GetTeam(ctx context.Context, teamID id.TeamID) (*organization.Team, error)

GetTeam returns a team by ID.

func (*Plugin) IsOrgSlugAvailable added in v1.9.0

func (p *Plugin) IsOrgSlugAvailable(ctx context.Context, appID id.AppID, slug string) (bool, error)

IsOrgSlugAvailable checks whether a slug is available for an app.

func (*Plugin) ListInvitations added in v1.9.0

func (p *Plugin) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)

ListInvitations lists invitations for an organization.

func (*Plugin) ListInvitationsPage added in v1.9.0

func (p *Plugin) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)

ListInvitationsPage is the bounded twin for the request path.

func (*Plugin) ListMembers added in v1.9.0

func (p *Plugin) ListMembers(ctx context.Context, orgID id.OrgID) ([]*organization.Member, error)

ListMembers returns all members of an organization.

func (*Plugin) ListMembersPage added in v1.9.0

func (p *Plugin) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)

ListMembersPage is the bounded twin for the request path.

func (*Plugin) ListTeams added in v1.9.0

func (p *Plugin) ListTeams(ctx context.Context, orgID id.OrgID) ([]*organization.Team, error)

ListTeams returns all teams in an organization.

func (*Plugin) ListTeamsPage added in v1.9.0

func (p *Plugin) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)

ListTeamsPage is the bounded twin for the request path.

func (*Plugin) ListUserOrganizations added in v1.9.0

func (p *Plugin) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)

ListUserOrganizations returns all organizations a user belongs to.

func (*Plugin) ListUserOrganizationsPage added in v1.9.0

func (p *Plugin) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)

ListUserOrganizationsPage is the bounded twin for the request path.

func (*Plugin) Name added in v1.9.0

func (p *Plugin) Name() string

Name returns the plugin name.

func (*Plugin) OnInit added in v1.9.0

func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error

OnInit captures engine capabilities for use by the plugin's service layer.

func (*Plugin) RegisterContract added in v1.9.0

func (p *Plugin) RegisterContract(
	d *dispatcher.Dispatcher,
	reg contract.Registry,
	wreg contract.WardenRegistry,
	engine plugin.Engine,
) error

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(router forge.Router) error

RegisterRoutes registers organization management routes on a forge.Router.

func (*Plugin) RemoveMember added in v1.9.0

func (p *Plugin) RemoveMember(ctx context.Context, memberID id.MemberID) error

RemoveMember removes a member from an organization.

func (*Plugin) ResendInvitation added in v1.11.0

func (p *Plugin) ResendInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)

ResendInvitation gives a pending or expired invitation a new token and a fresh Config.InvitationTTL, so the link already sent stops working. The returned invitation carries the new plaintext Token; it is the only copy, so whoever delivers the invitation must take it from here. Accepted, declined and revoked invitations return ErrInvitationNotActionable, and a second send inside ResendInvitationInterval returns ErrInvitationResendTooSoon.

func (*Plugin) RevokeInvitation added in v1.11.0

func (p *Plugin) RevokeInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)

RevokeInvitation withdraws a pending or expired invitation. The row stays, marked revoked, so the audit trail has something to point at; its token stops working. Anything else returns ErrInvitationNotActionable.

func (*Plugin) SetStore added in v1.9.0

func (p *Plugin) SetStore(s store.Store)

SetStore allows direct store injection for testing.

func (*Plugin) UpdateMemberRole added in v1.9.0

func (p *Plugin) UpdateMemberRole(ctx context.Context, memberID id.MemberID, role organization.MemberRole) (*organization.Member, error)

UpdateMemberRole updates a member's role within an organization.

func (*Plugin) UpdateOrganization added in v1.9.0

func (p *Plugin) UpdateOrganization(ctx context.Context, o *organization.Organization) error

UpdateOrganization updates an existing organization.

func (*Plugin) UpdateTeam added in v1.9.0

func (p *Plugin) UpdateTeam(ctx context.Context, t *organization.Team) error

UpdateTeam updates an existing team.

type RemoveMemberRequest added in v0.0.7

type RemoveMemberRequest struct {
	OrgID    string `path:"orgId" description:"Organization identifier"`
	MemberID string `path:"memberId" description:"Member identifier"`
}

RemoveMemberRequest binds the path for DELETE /orgs/:orgId/members/:memberId.

type ResendInvitationRequest added in v1.11.0

type ResendInvitationRequest struct {
	OrgID        string `path:"orgId" description:"Organization identifier"`
	InvitationID string `path:"invitationId" description:"Invitation identifier"`
}

ResendInvitationRequest binds the path for POST /orgs/:orgId/invitations/:invitationId/resend.

type RevokeInvitationRequest added in v1.11.0

type RevokeInvitationRequest struct {
	OrgID        string `path:"orgId" description:"Organization identifier"`
	InvitationID string `path:"invitationId" description:"Invitation identifier"`
}

RevokeInvitationRequest binds the path for DELETE /orgs/:orgId/invitations/:invitationId.

type SlugAvailableResponse added in v1.9.0

type SlugAvailableResponse struct {
	Available bool `json:"available" description:"Whether the slug is available"`
}

SlugAvailableResponse reports whether a slug is available.

type StatusResponse

type StatusResponse struct {
	Status string `json:"status" description:"Operation status"`
}

StatusResponse is a generic status response.

type TeamListResponse added in v1.9.0

type TeamListResponse struct {
	Teams any `json:"teams" description:"List of teams"`
	// NextCursor is set when more remain; pass it as cursor to continue.
	NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}

TeamListResponse wraps a list of teams.

type UpdateMemberRequest

type UpdateMemberRequest struct {
	OrgID    string `path:"orgId" description:"Organization identifier"`
	MemberID string `path:"memberId" description:"Member identifier"`
	Role     string `json:"role" description:"New member role (owner, admin, member)"`
}

UpdateMemberRequest binds path + body for PATCH /orgs/:orgId/members/:memberId.

type UpdateOrgRequest added in v1.9.0

type UpdateOrgRequest struct {
	OrgID string  `path:"orgId" description:"Organization identifier"`
	Name  *string `json:"name,omitempty" description:"Organization name"`
}

UpdateOrgRequest binds path + body for PATCH /orgs/:orgId.

type UpdateTeamRequest

type UpdateTeamRequest struct {
	OrgID  string  `path:"orgId" description:"Organization identifier"`
	TeamID string  `path:"teamId" description:"Team identifier"`
	Name   *string `json:"name,omitempty" description:"Team name"`
	Slug   *string `json:"slug,omitempty" description:"URL-safe team slug"`
}

UpdateTeamRequest binds path + body for PATCH /orgs/:orgId/teams/:teamId.

Directories

Path Synopsis
Package contract wires the organization plugin's intent surface into the forge-dashboard contract registry.
Package contract wires the organization plugin's intent surface into the forge-dashboard contract registry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL