Elaichi Roles API

The Roles API from Elaichi — 3 operation(s) for roles.

GitHub OpenAPI

Operations 6

GET /role List roles #
POST /role Create role #
GET /role/{id} Get role #
PATCH /role/{id} Update role #
DELETE /role/{id} Delete role #
POST /role/{id}/delete Delete a role, with a JSON body #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/elaichi-ai:elaichi-ai-roles-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

elaichi-ai-roles-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Elaichi Roles API
  version: 0.1.0
  description: 'Control-plane HTTP API for Elaichi, the MCP control plane for SaaS teams. Authenticate with an organization API token (create one in the Elaichi app under Settings → API tokens) sent as a bearer token, plus the `X-Organization-Id` header on org-scoped routes; the MCP and OAuth endpoints use their own protocols. A few actions are deliberately not available to an API token and must be done by a person in the Elaichi app: managing API and SCIM tokens, creating an organization, approving an access request, deleting an organization, and changing the two-factor requirement. The operations concerned say so. Endpoints used only by the Elaichi app itself (sign-in, consent screens, browser redirects, inbound webhooks) are not documented.'
servers:
- url: https://api.elaichi.ai
  description: Production
tags:
- name: Roles
paths:
  /role:
    get:
      operationId: listRoles
      summary: List roles
      tags:
      - Roles
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - result
                - next_cursor
                - prev_cursor
                properties:
                  result:
                    type: array
                    items:
                      $ref: '#/components/schemas/Role'
                  next_cursor:
                    type:
                    - string
                    - 'null'
                  prev_cursor:
                    type:
                    - string
                    - 'null'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: Roles in this organization, system and custom alike. Any member may read them — this is what populates a role picker before `PATCH /member/{userId}` or `POST /invite`.
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
      - $ref: '#/components/parameters/LimitQuery'
      - $ref: '#/components/parameters/CursorQuery'
    post:
      operationId: createRole
      summary: Create role
      tags:
      - Roles
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Role'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: Authoring custom roles requires the `role:manage` permission and the `custom_roles` plan feature; the predefined system roles stay readable and assignable on every plan. You may only grant permissions you hold yourself — otherwise an admin could mint a role carrying `billing:manage` and assign it to themselves. Validate names against `GET /permission`.
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - permissions
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                description:
                  type:
                  - string
                  - 'null'
                  maxLength: 2000
                permissions:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                  description: Permission names from `GET /permission`. The complete set for this role.
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
  /role/{id}:
    get:
      operationId: getRole
      summary: Get role
      tags:
      - Roles
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Role'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: Returns one role with its permissions and member counts. Any member may read it.
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Role id (`role_…`).
    patch:
      operationId: patchRole
      summary: Update role
      tags:
      - Roles
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Role'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: 'Updates a custom role. **`permissions` is REPLACED wholesale, not merged** — the array you send becomes the role''s entire permission set, so anything omitted is revoked from every member holding the role. Read the role first and send the full intended list. Omitting `permissions` entirely leaves it untouched. Edits apply immediately: effective permissions are recomputed on every request. Requires `role:manage`, the `custom_roles` feature, and that you hold every permission you are granting.'
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                description:
                  type:
                  - string
                  - 'null'
                  maxLength: 2000
                permissions:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                  description: Replaces the role's permission set entirely.
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Role id (`role_…`).
    delete:
      operationId: deleteRole
      summary: Delete role
      tags:
      - Roles
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - success
                - reassigned_member_count
                - reassigned_invite_count
                properties:
                  success:
                    type: boolean
                  reassigned_member_count:
                    type: integer
                    description: How many members were moved onto `reassign_to`. 0 when none held the role.
                  reassigned_invite_count:
                    type: integer
                    description: How many pending invites were re-pointed at `reassign_to`.
                  reassigned_by_role:
                    type: array
                    items:
                      type: object
                      properties:
                        role_id:
                          type: string
                        role_name:
                          type: string
                        count:
                          type: integer
                    description: One row per distinct role members were moved onto (just `reassign_to` here). Empty when nobody held the role.
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: 'Deletes a custom role. Irreversible. A member holds exactly one role, so a role anybody still holds cannot simply disappear: without `reassign_to` the call answers `400 role_in_use` while `member_count` or `pending_invite_count` is above zero (`GET /member?role_id={id}` lists the holders). With `reassign_to`, every holder and every pending invite for the role is moved onto that role and the role is deleted in one atomic operation — everything moves and the role is gone, or nothing changes. The move takes effect on each member''s next request. A role that a SCIM group mapping assigns (`group_mapping_count` above zero) is refused with `409 role_mapped`, `reassign_to` or not: change the mapping first. Requires `role:manage` and the `custom_roles` feature. Sending `reassign_to` at all additionally requires `member:manage` and that you hold every permission the replacement role carries (the `PATCH /member/{userId}` rules), and the target must exist and differ from `id` — checked whether or not anything needs moving. It is further refused if you hold the deleted role yourself, if the replacement is Org Owner and you are not one, or if it would leave no billable member. System roles cannot be deleted. This is the query-parameter-only form; `POST /role/{id}/delete` is the same operation with a JSON body, for a client that also wants to move specific holders onto a role of their own choosing. A browser session must also pass a fresh step-up reauthentication in the app (without it the call answers `428 step_up_required`). An organization API token is not challenged.'
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Role id (`role_…`).
      - name: reassign_to
        in: query
        required: false
        description: Role id (`role_…`) to move every current holder and pending invite onto before deleting. Must exist and differ from `id`; its authorization (`member:manage` and the grant ceiling) applies even when nothing holds the role, in which case nothing is moved.
        schema:
          type: string
  /role/{id}/delete:
    post:
      operationId: deleteRoleStructured
      summary: Delete a role, with a JSON body
      tags:
      - Roles
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - success
                - reassigned_member_count
                - reassigned_invite_count
                - reassigned_by_role
                properties:
                  success:
                    type: boolean
                  reassigned_member_count:
                    type: integer
                  reassigned_invite_count:
                    type: integer
                  reassigned_by_role:
                    type: array
                    items:
                      type: object
                      properties:
                        role_id:
                          type: string
                        role_name:
                          type: string
                        count:
                          type: integer
                    description: One row per distinct target role that received at least one member.
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      description: 'Deletes a role, taking a JSON body. The same operation as `DELETE /role/{id}` — same authorization, same guards, same atomic transaction — with a JSON body instead of a query parameter, so a caller can also move SPECIFIC holders onto a role of their own choosing before the rest fall back to `reassign_to`. `reassignments` is capped at 200 entries; past it the call answers `400` naming the ceiling. Each entry''s `role_id` is checked against the same grant ceiling as `reassign_to` (never the role being deleted), and each `user_id` must currently hold the role — one that no longer does answers `409` (`role_holder_changed`), so the caller can refresh and retry. `reassign_to` becomes optional once every holder is covered by `reassignments` AND no pending invite is for the role (invites are never individually overridden, and always fall back to `reassign_to`); short of that it is still required, exactly as on the plain DELETE form. The response adds `reassigned_by_role`: one row per DISTINCT target role actually used, bounded by how many roles the caller named.'
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                reassign_to:
                  type: string
                  description: Same as `DELETE /role/{id}`'s `reassign_to` query parameter — the default target.
                reassignments:
                  type: array
                  maxItems: 200
                  items:
                    type: object
                    required:
                    - user_id
                    - role_id
                    properties:
                      user_id:
                        type: string
                        description: User id (`usr_…`) of a current holder of `id`.
                      role_id:
                        type: string
                        description: Role id (`role_…`) to move that one member onto.
                  description: Per-member overrides. Bounded, and never a substitute for `reassign_to` on invites.
      parameters:
      - $ref: '#/components/parameters/OrganizationIdHeader'
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Role id (`role_…`) being deleted.
components:
  parameters:
    OrganizationIdHeader:
      name: X-Organization-Id
      in: header
      required: false
      description: Target organization (`org_…`). Required for cookie/session auth on org-scoped routes; optional for API tokens, which carry their organization, and rejected when it names a different one.
      schema:
        type: string
    CursorQuery:
      name: cursor
      in: query
      required: false
      description: Opaque cursor — pass the `next_cursor` from a previous response. Ignored by unpaginated endpoints.
      schema:
        type: string
    LimitQuery:
      name: limit
      in: query
      required: false
      description: Page size. Defaults to 50. Values above 200 are silently clamped to 200 rather than rejected; a value below 1, or a non-integer, is a 400. Ignored by the endpoints documented as unpaginated.
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
  schemas:
    Error:
      type: object
      required:
      - error
      description: The Elaichi error envelope. The MCP OAuth endpoints under `/oauth` are the exception — they return RFC 6749 errors instead (see `OAuthError`).
      properties:
        error:
          type: object
          required:
          - message
          - code
          properties:
            message:
              type: string
              description: Human-readable. 5xx messages are deliberately generic.
            code:
              type: string
              description: Stable machine-readable code, e.g. `not_found`, `subscription_required`.
            details:
              description: Structured detail for codes that carry one, e.g. `toolboxes_need_resolution`.
            feature:
              type: string
              description: The gated feature, on entitlement failures.
            plan:
              type:
              - string
              - 'null'
              enum:
              - personal
              - gold
              - black
              - null
          additionalProperties: true
    Role:
      type: object
      properties:
        id:
          type: string
          description: Role id (`role_…`).
        name:
          type: string
        description:
          type:
          - string
          - 'null'
        is_system:
          type: boolean
          description: Predefined roles ship with every organization and cannot be edited away.
        seat_class:
          type: string
          enum:
          - billable
          - free_admin
          description: Whether members holding only this role count toward billable seats.
        permissions:
          type: array
          items:
            type: string
          description: Every permission the role holds, including any `GET /permission` does not list for this organization (for example the Apps permissions while Apps are unavailable). `PATCH /role/{id}` replaces this list wholesale, so send back the entries you did not mean to remove.
        permission_count:
          type: integer
          description: 'How many of `permissions` `GET /permission` lists for this organization: the number to show beside the role. Can be lower than `permissions.length`.'
        member_count:
          type: integer
          description: How many members hold this role right now. `GET /member?role_id={id}` lists them; while it is above zero, `DELETE /role/{id}` needs `reassign_to` (or, on `POST /role/{id}/delete`, `reassign_to` and/or a `reassignments` entry for every holder).
        pending_invite_count:
          type: integer
          description: Pending invites issued for this role. Present only on roles you can manage. While above zero, `DELETE /role/{id}` and `POST /role/{id}/delete` need `reassign_to`, which re-points them.
        group_mapping_count:
          type: integer
          description: SCIM group mappings that assign this role. Present only on roles you can manage. While above zero, `DELETE /role/{id}` is refused (`409 role_mapped`) until the mappings change.
        can_reassign_holders:
          type: boolean
          description: 'Whether you may move this role''s holders and pending invites to another role (`DELETE /role/{id}?reassign_to=` or `POST /role/{id}/delete`): you hold `member:manage` and do not hold this role yourself.'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: session or API token
      description: '`Authorization: Bearer <token>` with an org-scoped API token (`elch_{org_id}_{64 hex}`), created in the Elaichi app under Settings → API tokens. The Elaichi app itself signs in with a session cookie, which is also accepted but is not meant for integrations. Two other credential families exist and are NOT interchangeable with this one: an MCP OAuth access token (`POST /mcp` only) and a SCIM token (`/scim/v2/*` only).'
    mcpOAuth:
      type: oauth2
      description: MCP OAuth access token, used only by `POST /mcp` — not interchangeable with `bearerAuth`. Discovered per RFC 9728/8414 at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`.
      flows:
        authorizationCode:
          authorizationUrl: https://api.elaichi.ai/oauth/authorize
          tokenUrl: https://api.elaichi.ai/oauth/token
          scopes:
            mcp:read: Anything you can already see in Elaichi. Never secret values. It can also ask an admin for access to something restricted for you.
            mcp:write: Create and change teams, roles, toolboxes, templates and connections, and share access.
            mcp:destructive: Permanently delete data and remove access in Elaichi. This cannot be undone.
            mcp:tools: Run tools from the toolboxes you choose next, deleting in your connected apps included. It can also ask an admin for access to something restricted for you.
            openid: That you are signed in, and the user ID we use for you. Nothing else.
            email: Your email address, and whether it is verified. Apps use this to check your domain.