> ## Documentation Index
> Fetch the complete documentation index at: https://handbook.nyotaimara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage organization members, roles, and permissions

> List members and pending invites, retrieve available roles and permissions, and create, update, or delete custom roles for your organization.

This page covers the endpoints for viewing your organization's team and managing its role-based access control (RBAC) configuration. The built-in roles — `owner`, `admin`, `billing`, and `member` — are protected and cannot be modified or deleted. You can create custom roles (e.g., `auditor`, `marketing`) and assign specific permissions to them.

***

## List members and pending invites

Returns all active members and all outstanding invitations for the organization.

### Endpoint

```
GET https://api.nyotaimara.com/v1/organizations/:id/members
```

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <token>`.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  The ID of the organization to query. Must match the `:id` path parameter.
</ParamField>

### Path parameters

<ParamField path="id" type="string" required>
  The unique identifier of the organization.
</ParamField>

### Response

<ResponseField name="success" type="boolean" required>
  `true` on success.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="data" defaultOpen>
    <ResponseField name="members" type="object[]">
      Active organization members.

      <Expandable title="members[]">
        <ResponseField name="id" type="string">
          User ID of the member.
        </ResponseField>

        <ResponseField name="name" type="string">
          First name of the member.
        </ResponseField>

        <ResponseField name="email" type="string">
          Email address.
        </ResponseField>

        <ResponseField name="avatarUrl" type="string">
          URL to the member's avatar image. May be `null`.
        </ResponseField>

        <ResponseField name="role" type="string">
          The member's assigned role name (e.g., `"admin"`, `"member"`).
        </ResponseField>

        <ResponseField name="joinedAt" type="string">
          ISO 8601 timestamp of when the user joined the organization.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="invites" type="object[]">
      Pending invitations that have not yet been accepted.

      <Expandable title="invites[]">
        <ResponseField name="id" type="string">
          Invite record ID.
        </ResponseField>

        <ResponseField name="email" type="string">
          Email address the invite was sent to.
        </ResponseField>

        <ResponseField name="role" type="string">
          Role that will be assigned upon acceptance.
        </ResponseField>

        <ResponseField name="expiresAt" type="string">
          ISO 8601 timestamp when the invite token expires.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.nyotaimara.com/v1/organizations/org_01j9kxyz/members \
    --header 'Authorization: Bearer <token>' \
    --header 'X-Organization-Id: org_01j9kxyz'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "data": {
      "members": [
        {
          "id": "usr_abc123",
          "name": "Amina",
          "email": "amina@acmekenya.co.ke",
          "avatarUrl": "https://cdn.nyotaimara.com/avatars/amina.jpg",
          "role": "owner",
          "joinedAt": "2024-01-15T09:30:00.000Z"
        }
      ],
      "invites": [
        {
          "id": "inv_xyz789",
          "email": "john@acmekenya.co.ke",
          "role": "admin",
          "expiresAt": "2024-02-01T09:30:00.000Z"
        }
      ]
    }
  }
  ```
</ResponseExample>

***

## Get roles and permissions

Returns all roles available to the organization and the full list of permissions that can be assigned to custom roles.

### Endpoint

```
GET https://api.nyotaimara.com/v1/organizations/iam/roles
```

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <token>`.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  Active organization context.
</ParamField>

### Response

<ResponseField name="success" type="boolean" required>
  `true` on success.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="data" defaultOpen>
    <ResponseField name="roles" type="object[]">
      All roles defined for the organization.

      <Expandable title="roles[]">
        <ResponseField name="id" type="string">
          Role ID.
        </ResponseField>

        <ResponseField name="name" type="string">
          Role name (e.g., `"admin"`, `"auditor"`).
        </ResponseField>

        <ResponseField name="isProtected" type="boolean">
          `true` for built-in roles (`owner`, `admin`, `billing`, `member`). Protected roles cannot be modified or deleted.
        </ResponseField>

        <ResponseField name="permissions" type="string[]">
          Array of permission IDs assigned to this role.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="permissions" type="object[]">
      All available permissions that can be assigned to custom roles.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.nyotaimara.com/v1/organizations/iam/roles \
    --header 'Authorization: Bearer <token>' \
    --header 'X-Organization-Id: org_01j9kxyz'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "data": {
      "roles": [
        {
          "id": "role_owner",
          "name": "owner",
          "isProtected": true,
          "permissions": ["perm_org_update", "perm_member_invite", "perm_member_read"]
        },
        {
          "id": "role_auditor",
          "name": "auditor",
          "isProtected": false,
          "permissions": ["perm_member_read"]
        }
      ],
      "permissions": [
        { "id": "perm_org_update", "name": "org:organization:update" },
        { "id": "perm_member_invite", "name": "org:member:invite" },
        { "id": "perm_member_read", "name": "org:member:read" }
      ]
    }
  }
  ```
</ResponseExample>

***

## Create a custom role

Creates a new custom role for the organization and optionally assigns permissions to it.

### Endpoint

```
POST https://api.nyotaimara.com/v1/organizations/iam/roles
```

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <token>`.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  Active organization context.
</ParamField>

### Body

<ParamField body="name" type="string" required>
  Name of the new role. The value is lowercased and non-alphanumeric characters (except underscores) are replaced with underscores. Reserved names (`owner`, `admin`, `billing`, `member`) are rejected.
</ParamField>

<ParamField body="description" type="string">
  Human-readable description of the role's purpose.
</ParamField>

<ParamField body="permissionIds" type="string[]">
  Array of permission IDs to assign to this role. Retrieve available IDs from `GET /v1/organizations/iam/roles`.
</ParamField>

### Response

<ResponseField name="success" type="boolean" required>
  `true` on successful creation.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message, e.g. `"Organization role 'auditor' created successfully."`.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.nyotaimara.com/v1/organizations/iam/roles \
    --header 'Authorization: Bearer <token>' \
    --header 'X-Organization-Id: org_01j9kxyz' \
    --header 'Content-Type: application/json' \
    --data '{
      "name": "auditor",
      "description": "Read-only access to financial and compliance data",
      "permissionIds": ["perm_member_read", "perm_kyb_read"]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "success": true,
    "message": "Organization role 'auditor' created successfully."
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": "Cannot create a role with a reserved system name."
  }
  ```
</ResponseExample>

***

## Update a custom role's permissions

Replaces all permissions assigned to a custom role. The existing permission set is fully replaced — include every permission you want the role to have.

### Endpoint

```
PATCH https://api.nyotaimara.com/v1/organizations/iam/roles/:roleId
```

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <token>`.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  Active organization context.
</ParamField>

### Path parameters

<ParamField path="roleId" type="string" required>
  The ID of the role to update.
</ParamField>

### Body

<ParamField body="permissionIds" type="string[]" required>
  Complete list of permission IDs to assign to the role. The previous permissions are replaced atomically.
</ParamField>

### Response

<ResponseField name="success" type="boolean" required>
  `true` on success.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message.
</ResponseField>

<Note>
  Protected roles (`owner`, `admin`, `billing`, `member`) cannot be updated. Attempting to do so returns a `403 Forbidden` error.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.nyotaimara.com/v1/organizations/iam/roles/role_auditor \
    --header 'Authorization: Bearer <token>' \
    --header 'X-Organization-Id: org_01j9kxyz' \
    --header 'Content-Type: application/json' \
    --data '{
      "permissionIds": ["perm_member_read", "perm_kyb_read", "perm_org_read"]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "message": "Role 'auditor' permissions updated."
  }
  ```

  ```json 403 theme={null}
  {
    "success": false,
    "error": "The system role 'admin' cannot be modified."
  }
  ```
</ResponseExample>

***

## Delete a custom role

Permanently deletes a custom role from the organization.

### Endpoint

```
DELETE https://api.nyotaimara.com/v1/organizations/iam/roles/:roleId
```

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <token>`.
</ParamField>

<ParamField header="X-Organization-Id" type="string" required>
  Active organization context.
</ParamField>

### Path parameters

<ParamField path="roleId" type="string" required>
  The ID of the custom role to delete.
</ParamField>

### Response

<ResponseField name="success" type="boolean" required>
  `true` on successful deletion.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message.
</ResponseField>

<Warning>
  You cannot delete a role while any users are still assigned to it. Reassign those users to a different role before deleting. Protected roles (`owner`, `admin`, `billing`, `member`) can never be deleted.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.nyotaimara.com/v1/organizations/iam/roles/role_auditor \
    --header 'Authorization: Bearer <token>' \
    --header 'X-Organization-Id: org_01j9kxyz'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "message": "Role 'auditor' deleted successfully."
  }
  ```

  ```json 403 theme={null}
  {
    "success": false,
    "error": "The system role 'member' cannot be deleted."
  }
  ```

  ```json 500 theme={null}
  {
    "success": false,
    "error": "Failed to delete role. Ensure no users are currently assigned to it."
  }
  ```
</ResponseExample>
