openapi: 3.2.0 info: title: Goalkeeper Organizations API version: 0.0.0 description: Public REST API. servers: - url: http://localhost:3001 description: Local development tags: - name: Organizations description: Organization membership and active organization selection. paths: /v1/organizations: get: operationId: listOrganizations summary: List organizations description: Lists the current user's organizations and active organization. Creates the user's first organization when none exists. tags: - Organizations responses: '200': description: Organization membership context. content: application/json: schema: $ref: '#/components/schemas/OrganizationContext' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' post: operationId: createOrganization summary: Create an organization description: Creates an organization owned by the current user and makes it active. tags: - Organizations requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateOrganizationRequest' responses: '201': description: The created organization is active. content: application/json: schema: $ref: '#/components/schemas/OrganizationContext' '400': description: The organization request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The request origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/switch: post: operationId: switchOrganization summary: Switch organizations description: Makes one of the current user's organization memberships active. tags: - Organizations requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SwitchOrganizationRequest' responses: '200': description: The selected organization is active. content: application/json: schema: $ref: '#/components/schemas/OrganizationContext' '400': description: The organization identifier is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The user is not a member or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current: patch: operationId: updateCurrentOrganization summary: Update the active organization description: Updates the active organization. The current user must be an owner or administrator. tags: - Organizations requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateOrganizationRequest' responses: '200': description: The active organization was updated. content: application/json: schema: $ref: '#/components/schemas/OrganizationContext' '400': description: The organization request is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Administrator access is required or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current/members: get: operationId: listCurrentOrganizationMembers summary: List active organization members description: Lists the user-to-organization memberships for the active organization. tags: - Organizations responses: '200': description: Active organization members. content: application/json: schema: $ref: '#/components/schemas/ListOrganizationMembersResponse' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Organization membership is required. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current/members/{userId}: patch: operationId: updateCurrentOrganizationMemberRole summary: Update an organization member role description: Updates a non-owner membership role. The current user must be an owner or administrator. tags: - Organizations parameters: - name: userId in: path required: true schema: type: string minLength: 1 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateOrganizationMemberRoleRequest' responses: '200': description: The membership role was updated. content: application/json: schema: $ref: '#/components/schemas/UpdateOrganizationMemberRoleResponse' '400': description: The member or role is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Administrator access is required, the owner role is immutable, or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current/invitations: get: operationId: listCurrentOrganizationInvitations summary: List pending invitations description: Lists pending invitations for the active organization. Any member may read them. Invitation tokens are never returned. tags: - Organizations responses: '200': description: The pending invitations were listed. content: application/json: schema: $ref: '#/components/schemas/ListOrganizationInvitationsResponse' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The user is not a member of the organization. content: application/json: schema: $ref: '#/components/schemas/Error' post: operationId: createCurrentOrganizationInvitation summary: Invite someone to the active organization description: Creates an invitation for an email address. The current user must be an owner or administrator. The acceptance link is returned only in this response; it cannot be recovered later. tags: - Organizations requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateOrganizationInvitationRequest' responses: '201': description: The invitation was created. content: application/json: schema: $ref: '#/components/schemas/IssuedOrganizationInvitation' '400': description: The email address or role is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Administrator access is required or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: An invitation for this address is already pending. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current/invitations/{invitationId}: delete: operationId: revokeCurrentOrganizationInvitation summary: Revoke a pending invitation description: Revokes a pending invitation. The current user must be an owner or administrator. tags: - Organizations parameters: - name: invitationId in: path required: true schema: type: string format: uuid responses: '204': description: The invitation was revoked. '400': description: The invitation identifier is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Administrator access is required, the invitation is not pending, or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/current/invitations/{invitationId}/resend: post: operationId: resendCurrentOrganizationInvitation summary: Reissue a pending invitation description: Issues a new token for a pending invitation, invalidating the previous link and extending the expiry. Required because the plaintext token is returned only once. tags: - Organizations parameters: - name: invitationId in: path required: true schema: type: string format: uuid responses: '200': description: The invitation was reissued with a new link. content: application/json: schema: $ref: '#/components/schemas/IssuedOrganizationInvitation' '400': description: The invitation identifier is invalid. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Administrator access is required, the invitation is not pending, or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' /v1/organizations/invitations/accept: post: operationId: acceptOrganizationInvitation summary: Accept an invitation description: Consumes an invitation token and joins the authenticated user to the organization, making it active. The token must have been issued to the session's verified email address. tags: - Organizations requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AcceptOrganizationInvitationRequest' responses: '200': description: The invitation was accepted. content: application/json: schema: $ref: '#/components/schemas/AcceptOrganizationInvitationResponse' '400': description: The token is malformed. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: The request is not authenticated. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: The invitation was issued to a different email address, or the origin is not allowed. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The invitation is expired, revoked, or already used. content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: The user already belongs to the organization. content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: SwitchOrganizationRequest: type: object additionalProperties: false required: - organizationId properties: organizationId: type: string format: uuid CreateOrganizationRequest: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 maxLength: 100 UpdateOrganizationRequest: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 maxLength: 100 OrganizationInvitation: type: object additionalProperties: false required: - id - email - role - status - invitedByUserId - expiresAt - createdAt properties: id: type: string format: uuid email: type: string format: email role: $ref: '#/components/schemas/InvitableOrganizationRole' status: type: string enum: - pending - accepted - revoked - expired invitedByUserId: type: string minLength: 1 expiresAt: type: string format: date-time createdAt: type: string format: date-time OrganizationMember: type: object additionalProperties: false required: - userId - displayName - email - role properties: userId: type: string minLength: 1 displayName: type: string minLength: 1 email: type: - string - 'null' format: email role: $ref: '#/components/schemas/OrganizationRole' ListOrganizationInvitationsResponse: type: object additionalProperties: false required: - invitations properties: invitations: type: array items: $ref: '#/components/schemas/OrganizationInvitation' IssuedOrganizationInvitation: type: object additionalProperties: false required: - invitation - acceptUrl - emailSent properties: invitation: $ref: '#/components/schemas/OrganizationInvitation' acceptUrl: type: string description: Single-use acceptance link. Returned only here; the token is stored hashed and cannot be recovered. Use the resend operation to issue a replacement. emailSent: type: boolean description: False when no mailer is configured or delivery failed. The invitation is still valid and the link above can be shared directly. OrganizationRole: type: string enum: - owner - admin - member AcceptOrganizationInvitationRequest: type: object additionalProperties: false required: - token properties: token: type: string minLength: 1 maxLength: 200 CreateOrganizationInvitationRequest: type: object additionalProperties: false required: - email - role properties: email: type: string format: email maxLength: 320 role: $ref: '#/components/schemas/InvitableOrganizationRole' UpdateOrganizationMemberRoleRequest: type: object additionalProperties: false required: - role properties: role: type: string enum: - admin - member AcceptOrganizationInvitationResponse: type: object additionalProperties: false required: - organizationId - role - activeOrganizationId - organizations properties: organizationId: type: string format: uuid role: $ref: '#/components/schemas/InvitableOrganizationRole' activeOrganizationId: type: string format: uuid organizations: type: array minItems: 1 items: $ref: '#/components/schemas/OrganizationSummary' OrganizationSummary: type: object additionalProperties: false required: - id - name - role properties: id: type: string format: uuid name: type: string minLength: 1 maxLength: 100 role: $ref: '#/components/schemas/OrganizationRole' OrganizationContext: type: object additionalProperties: false required: - activeOrganizationId - organizations properties: activeOrganizationId: type: string format: uuid organizations: type: array minItems: 1 items: $ref: '#/components/schemas/OrganizationSummary' ListOrganizationMembersResponse: type: object additionalProperties: false required: - members properties: members: type: array minItems: 1 items: $ref: '#/components/schemas/OrganizationMember' Error: type: object additionalProperties: false required: - error properties: error: type: string message: type: string UpdateOrganizationMemberRoleResponse: type: object additionalProperties: false required: - member properties: member: $ref: '#/components/schemas/OrganizationMember' InvitableOrganizationRole: type: string enum: - admin - member securitySchemes: bearerAuth: type: http scheme: bearer description: A Goalkeeper API token or provider-issued OAuth access token. cookieAuth: type: apiKey in: cookie name: goalkeeper_session