Organizations

Endpoints for creating and managing organizations, their members, avatars, Rancher integration tokens, and organization-scoped access/NATS tokens.

All paths below are relative to the API root /api/v1. For example, GET /orgs/{orgname} is GET https://<akp-host>/api/v1/orgs/{orgname}. In these paths orgname is the organization slug (e.g. appscode).

Unless noted, authenticated endpoints accept a personal access token:

curl -H "Authorization: token $AKP_TOKEN" \
  https://<akp-host>/api/v1/orgs/appscode

Authorization checks (e.g. edit:org, admin:org, view:avatar) are relationship-based (OpenFGA) and evaluated against the caller’s role in the organization.

Organization lifecycle

POST /orgs

Create a new organization owned by the authenticated user.

  • Auth: token. The user must be permitted to create organizations (CanCreateOrganization); otherwise 403 is returned.

Request body (CreateOrgOption):

{
  "username": "acme",
  "full_name": "Acme Inc.",
  "description": "Acme engineering org",
  "website": "https://acme.example.com",
  "location": "Remote",
  "orgType": "public",
  "visibility": "public"
}
FieldTypeRequiredDescription
usernamestringyesSlug/username of the new organization.
full_namestringnoDisplay name.
descriptionstringnoFree-text description.
websitestringnoWebsite URL.
locationstringnoLocation text.
orgTypestringnoOne of public (default), limited, or private.
rancherManagementClusterEndPointstringnoRancher management cluster endpoint (for Rancher-origin orgs).
visibilitystringnoOne of "", public, limited, private.

Response: 201 Created with the created Organization:

{
  "id": 42,
  "username": "acme",
  "full_name": "Acme Inc.",
  "avatar_url": "https://<akp-host>/accounts/avatars?obj=avatars/42-<hash>",
  "description": "Acme engineering org",
  "website": "https://acme.example.com",
  "location": "Remote",
  "rancherManagementClusterEndPoint": "",
  "visibility": "public",
  "orgType": 0
}

PATCH /orgs/claim/{claimID}

Claim a previously created standalone (marketplace) organization and assign ownership to the authenticated user.

  • Auth: token. Only registered on AppsCode-hosted deployments (setting.AppsCodeHosted).

Path parameters:

NameTypeDescription
claimIDstringClaim identifier of the standalone organization.

Response: 200 OK returning the claimed organization as a free-form Kubernetes object (models.User.APIFormat, dynamic payload). 409 Conflict if the organization was already claimed; 404 if the claim ID is unknown.

GET /orgs/claim/

Return the claim ID of the oldest claimable standalone organization, by proxying an eligibility check to the central marketplace server.

  • Auth: token + site admin (reqSiteAdmin). Only registered for marketplace deployments or in dev run mode.

Response: 200 OK (OrgClaimableCheckResp):

{
  "isClaimable": true,
  "claimID": "a1b2c3d4"
}

Verified: not called against the live platform (registered only on marketplace/dev deployments and requires site admin).

GET /orgs/{orgname}

Get an organization’s details (returned if visible to the requesting user).

  • Auth: public (no auth required). A non-public org may return 404 to callers who cannot see it.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK (Organization):

{
  "id": 3,
  "username": "appscode",
  "full_name": "",
  "avatar_url": "https://<akp-host>/accounts/avatars?obj=avatars/3-<hash>",
  "description": "",
  "website": "",
  "location": "",
  "rancherManagementClusterEndPoint": "",
  "visibility": "public",
  "orgType": 6
}

Verified: GET returned 200 for appscode on 2026-07-14.

PATCH /orgs/{orgname}

Update an organization’s profile fields.

  • Auth: token + authzCheck(edit:org) (Organization_CanEditOrg).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body (EditOrgOption):

{
  "full_name": "Acme Inc.",
  "description": "Updated description",
  "website": "https://acme.example.com",
  "location": "Remote",
  "orgType": "limited",
  "visibility": "limited"
}
FieldTypeRequiredDescription
full_namestringnoDisplay name.
descriptionstringnoFree-text description.
websitestringnoWebsite URL.
locationstringnoLocation text.
orgTypestringnoOne of public, limited, or private.
rancherManagementClusterEndPointstringnoRancher management cluster endpoint.
visibilitystringnoOne of "", public, limited, private.

Response: 200 OK with the updated Organization (same shape as GET /orgs/{orgname}).

DELETE /orgs/{orgname}

Delete an organization.

  • Auth: token + authzCheck(delete:org) (Organization_CanDeleteOrg). The requester must be the last admin (per the orgLastAdmin condition).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 204 No Content on success.

Membership & ownership checks

GET /orgs/{orgname}/user/{username}

Check whether a user exists (in the org context). Looks up a user by username and returns their profile if the user exists and is active.

  • Auth: token (org context).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringUsername to look up.

Response: 200 OK (Profile):

{
  "name": "someuser",
  "full_name": "Some User",
  "email": "user@example.com",
  "keep_email_private": false,
  "website": "",
  "location": "",
  "language": "en-US",
  "description": ""
}

400 if the user does not exist or is inactive.

Verified: GET returned 200 for appscode/appscode on 2026-07-14.

GET /orgs/{orgname}/is-owner

Return 200 if the authenticated user is an owner of the organization. Ownership is enforced by upstream middleware.

  • Auth: token (org context).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK (empty body) when the caller is an owner; otherwise 403.

Verified: GET returned 200 for appscode on 2026-07-14.

GET /orgs/{orgname}/members

List an organization’s members. Non-members (and unauthenticated callers) see only public members.

  • Auth: public (no auth required); the response is scoped to what the caller may see.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK — an array of User:

[
  {
    "id": 2,
    "login": "someuser",
    "username": "someuser",
    "full_name": "Some User",
    "email": "user@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/<hash>?d=identicon",
    "language": "en-US",
    "is_admin": true,
    "last_login": "2026-07-13T18:23:53Z",
    "created": "2026-07-05T06:46:29Z",
    "type": 0,
    "active": true,
    "prohibit_login": false,
    "location": "",
    "website": "",
    "description": "",
    "orgAdmin": true,
    "clientOrgUser": false
  }
]

username is a backward-compatibility alias of login.

Verified: GET returned 200 for appscode on 2026-07-14.

POST /orgs/{orgname}/members/action/{action}

Perform a membership action for a member identified by the uid query parameter.

  • Auth: token + org membership.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
actionstringOne of private, public, remove, leave.

Query parameters:

NameTypeRequiredDescription
uidinteger (int64)yesUser ID of the member the action applies to.

Response: 200 OK when the action is performed. 400 if uid is missing or invalid.

GET /orgs/{orgname}/members/{username}

Check whether a user is a member of an organization.

  • Auth: public (no auth required).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringUsername to check.

Response: 204 No Content if the user is a member; 404 if not; may redirect (302) to the public-members check for non-members of the org.

Verified: GET returned 204 for member appscode/someuser on 2026-07-14.

DELETE /orgs/{orgname}/members/{username}

Remove a member from the organization.

  • Auth: token + org ownership + authzCheck(admin:org) (Organization_CanRemoveMember, orgLastMember condition). Site admins cannot be removed from the administrative org.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringUsername of the member to remove.

Response: 204 No Content on success.

GET /orgs/{orgname}/public_members

List an organization’s public members.

  • Auth: public (no auth required).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK — an array of User (same shape as GET /orgs/{orgname}/members).

Verified: GET returned 200 for appscode on 2026-07-14.

GET /orgs/{orgname}/public_members/{username}

Check whether a user is a public member of an organization.

  • Auth: public (no auth required).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringUsername to check.

Response: 204 No Content if the user is a public member; 404 if not.

Verified: GET returned 404 for appscode/someuser on 2026-07-14 (member is not public).

PUT /orgs/{orgname}/public_members/{username}

Publicize the caller’s own organization membership.

  • Auth: token + org membership. A user cannot publicize another member (403).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringThe caller’s own username.

Response: 204 No Content on success.

DELETE /orgs/{orgname}/public_members/{username}

Conceal (make private) the caller’s own organization membership.

  • Auth: token + org membership. A user cannot conceal another member (403).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
usernamestringThe caller’s own username.

Response: 204 No Content on success.

Organization avatar

GET /orgs/{orgname}/avatar/

Get the organization’s avatar URL.

  • Auth: token + authzCheck(view:avatar) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK — a JSON string containing the avatar URL:

"https://<akp-host>/accounts/avatars?obj=avatars/3-<hash>"

POST /orgs/{orgname}/avatar/

Upload or set the organization avatar.

  • Auth: token + authzCheck(update:avatar) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body: multipart/form-data (AvatarParams):

FieldTypeRequiredDescription
avatarbinary (file)noUploaded avatar image file.
sourcestringnoAvatar source selector.
gravatarstringnoGravatar email/URL.
federavatarbooleannoWhether to use a federated avatar.

Response: 200 OK; the exact body depends on the avatar handler (a free-form Kubernetes object).

POST /orgs/{orgname}/avatar/delete

Remove the organization’s avatar.

  • Auth: token + authzCheck(delete:avatar) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK on success.

Rancher integration tokens

GET /orgs/{orgname}/rancher/sync-token

Get the organization’s Rancher synchronization token.

  • Auth: token + authzCheck(view:sync-token) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK (RancherAPIToken):

{
  "userID": 4,
  "orgID": 3,
  "orgName": "appscode",
  "accessKeyID": "<access-key-id>",
  "secretAccessKey": "<secret>"
}

404 when no sync token exists (e.g. the org is not of Rancher origin).

Verified: GET returned 404 for appscode on 2026-07-14 (org is not of Rancher origin — no sync token).

POST /orgs/{orgname}/rancher/sync-token

Create a Rancher organization synchronization token from the provided access key. The organization must be of Rancher origin.

  • Auth: token + authzCheck(create:rancher-org-sync-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body (AccessToken):

{
  "name": "rancher-sync",
  "sha1": "<rancher-access-key>"
}
FieldTypeRequiredDescription
idinteger (int64)noToken ID.
namestringnoToken name.
sha1stringnoThe plaintext access key.
token_last_eightstringnoLast eight characters of the token.

Response: 200 OK (ApiResponseMessage):

{ "message": "token created" }

400 if the organization’s origin is not Rancher.

DELETE /orgs/{orgname}/rancher/sync-token

Delete the organization’s Rancher synchronization token.

  • Auth: token + authzCheck(remove:rancher-sync-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK on success.

GET /orgs/{orgname}/rancher/user-token

Deprecated. Return the caller’s Rancher API token for the organization.

  • Auth: token + org membership + authzCheck(view:sync-token) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK (RancherAPIToken, same shape as the sync-token response).

Verified: GET returned 404 for appscode on 2026-07-14 (deprecated; org is not of Rancher origin).

POST /orgs/{orgname}/rancher/user-token

Deprecated. Store a Rancher API token for the caller in the organization.

  • Auth: token + org membership + authzCheck(create:rancher-org-sync-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body (AccessToken): same as POST /orgs/{orgname}/rancher/sync-token.

Response: 200 OK on success.

DELETE /orgs/{orgname}/rancher/user-token

Deprecated. Delete the caller’s Rancher API token for the organization.

  • Auth: token + org membership + authzCheck(remove:rancher-sync-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK on success.

Organization access & NATS tokens

These endpoints manage tokens belonging to the organization’s system admin.

GET /orgs/{orgname}/tokens/access-tokens/

List the access tokens of the organization’s system admin.

  • Auth: token + org membership + authzCheck(list:access-token) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK — an array of AccessTokenInfo:

[
  {
    "id": 12,
    "uid": 4,
    "name": "kube-ui-server 2026-07-05 7:49AM",
    "token": "",
    "tokenLastEight": "3d492e7c",
    "createdAt": 1783237775,
    "updatedAt": 1783785356,
    "hasRecentActivity": true,
    "hasUsed": true,
    "expDate": 2098856975
  }
]

The token value is empty for existing tokens; the full token is only returned at creation time. createdAt, updatedAt, and expDate are Unix timestamps. 400 if the organization system admin does not exist.

Verified: GET returned 200 for appscode on 2026-07-14.

DELETE /orgs/{orgname}/tokens/access-tokens/{id}

Delete an access token of the organization’s system admin.

  • Auth: token + org membership + authzCheck(delete:access-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
idinteger (int64)ID of the access token.

Response: 200 OK on success. 400 if the organization system admin does not exist.

GET /orgs/{orgname}/tokens/nats-tokens/

List the NATS user-type tokens of the organization’s system admin.

  • Auth: token + org membership + authzCheck(list:nats-token) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK — an array of NatsAccount:

[
  {
    "id": 1,
    "uid": 4,
    "accountType": "user",
    "natsPubKey": "<nats-public-key>",
    "clusterID": "<cluster-uid>",
    "productName": "kubedb",
    "revoked": false
  }
]

400 if the organization system admin does not exist.

Verified: GET returned 200 for appscode on 2026-07-14 (empty list []).

POST /orgs/{orgname}/tokens/nats-tokens/{id}/revoke

Revoke a NATS user-type token of the organization’s system admin.

  • Auth: token + org membership + authzCheck(revoke:nats-token) (Organization_Editor).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.
idinteger (int64)ID of the NATS token.

Response: 200 OK on success. 400 if the organization system admin is missing or the NATS user is not found.

Organization SSO auth source

An organization may own one authentication source, used to sign its members in through an external OAuth2/OIDC provider instead of a password. When enforceForMembers is set, password login is blocked for every member of the org and GET /user/login-method reports sso for them (see Authenticated User).

The client secret is write-only: it is never returned by any of these endpoints.

OrgAuthSourceResponse — the shape returned by GET, POST, and PUT:

FieldTypeDescription
idinteger (int64)Auth source ID (the org’s own ID, not a global login_source).
orgIDinteger (int64)Owning organization ID.
namestringHuman-readable name, e.g. Acme SSO.
providerstringgoth/OAuth2 provider type, e.g. openidConnect.
clientIDstringOAuth2 client ID.
discoveryURLstringOIDC discovery document URL; omitted when empty.
isActivebooleanWhether the source is active. Set to true on create.
enforceForMembersbooleanWhen true, blocks password login for all org members.

GET /orgs/{orgname}/auth-source

Get the organization’s authentication source.

  • Auth: token + authzCheck(view:auth-source) (Organization_Viewer).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK (OrgAuthSourceResponse):

{
  "id": 7,
  "orgID": 3,
  "name": "Acme SSO",
  "provider": "openidConnect",
  "clientID": "<client-id>",
  "discoveryURL": "https://sso.example.com/.well-known/openid-configuration",
  "isActive": true,
  "enforceForMembers": true
}

404 when the organization has no authentication source.

POST /orgs/{orgname}/auth-source

Create the organization’s authentication source. Fails with 409 Conflict if one already exists — use PUT to change it.

  • Auth: token + authzCheck(create:auth-source) (Organization_CanEditOrg).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body (OrgAuthSourceOption):

{
  "name": "Acme SSO",
  "provider": "openidConnect",
  "clientID": "<client-id>",
  "clientSecret": "<client-secret>",
  "discoveryURL": "https://sso.example.com/.well-known/openid-configuration",
  "enforceForMembers": true
}
FieldTypeRequiredDescription
namestringyesDisplay name of the source.
providerstringyesgoth/OAuth2 provider type, e.g. openidConnect.
clientIDstringyesOAuth2 client ID.
clientSecretstringyesOAuth2 client secret. Never returned in a response.
discoveryURLstringnoRequired in practice for OIDC providers.
enforceForMembersbooleannoBlock password login for all org members.

Response: 201 Created (OrgAuthSourceResponse, with isActive: true).

409 if a source already exists for the organization.

PUT /orgs/{orgname}/auth-source

Replace the organization’s authentication source settings.

  • Auth: token + authzCheck(update:auth-source) (Organization_CanEditOrg).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Request body (OrgAuthSourceUpdateOption) — same fields as the create body, except that clientSecret is optional: omit it or send an empty string to keep the stored secret.

Response: 200 OK (OrgAuthSourceResponse).

404 when the organization has no authentication source to update.

DELETE /orgs/{orgname}/auth-source

Remove the organization’s authentication source. Members fall back to password login.

  • Auth: token + authzCheck(delete:auth-source) (Organization_CanEditOrg).

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 204 No Content on success.

Organization inbox subscription

Subscribe the calling user to the organization’s inbox notification group. Only individual users can subscribe; a request made as an organization account returns 403. All three endpoints require the platform’s inbox service to be reachable.

Cluster-, namespace-, and resource-scoped subscriptions live on the Cluster Subscriptions page; the list of everything the caller is subscribed to is GET /user/inbox/subscriptions.

GET /orgs/{orgname}/subscription

Check whether the calling user is subscribed to the organization.

  • Auth: token + org membership.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK with an empty body when the subscription exists.

403 if the caller is not an individual user; 400 if orgname does not resolve to an organization.

POST /orgs/{orgname}/subscription

Subscribe the calling user to the organization.

  • Auth: token + org membership.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK with an empty body. Takes no request body.

403 if the caller is not an individual user; 400 if orgname does not resolve to an organization.

DELETE /orgs/{orgname}/subscription

Unsubscribe the calling user from the organization.

  • Auth: token + org membership.

Path parameters:

NameTypeDescription
orgnamestringOrganization slug.

Response: 200 OK with an empty body.

403 if the caller is not an individual user; 400 if orgname does not resolve to an organization.