Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: MindCloud Public Members API
version: 2.0.0
description: REST surface under /v2. Authenticate with a company-scoped API key sent as a bearer token. Each key holds a set of permissions; each operation lists the ones it requires as the apiKey security roles. A request may use a permission only while the user who created the key also holds it.
servers:
- url: https://connect.mindcloud.co
security:
- apiKey: []
tags:
- name: Members
paths:
/v2/members:
get:
operationId: v2-list-members
summary: List members of the company
description: 'List members of the company.
Each filterable field is also a query parameter: `status=active` matches it exactly, and bracket operators (eq, ne, gt, gte, lt, lte, contains, starts, ends, in, nin) compare, e.g. `createdOn[gte]=2026-01-01` or `status[in]=failed,cancelled`. contains, starts, and ends apply to text fields only.
Requires the API key permission member:view.'
tags:
- Members
security:
- apiKey:
- member:view
parameters:
- name: fields
in: query
required: false
schema:
type: string
description: 'Comma-separated fields to return; all of them when left out. Each item then carries only those keys, whatever its schema requires. Fields: userId, name, email, isPending, roleId, roleName, createdOn.'
- $ref: '#/components/parameters/limit'
- $ref: '#/components/parameters/offset'
- name: sort
in: query
required: false
schema:
type: string
description: 'Comma-separated sort fields; prefix one with - for descending. Sortable fields: userId, name, email, isPending, roleId, roleName, createdOn. Default: -createdOn,userId.'
- name: where
in: query
required: false
schema:
type: string
maxLength: 2048
description: 'RSQL filter expression. Filterable fields: userId, name, email, isPending, roleId, roleName, createdOn. Operators: ==, !=, >, >=, <, <=, =in=, =out=, =like=. Logical: ; (AND), , (OR); AND binds tighter than OR. Example: "status==active;name=like=Sales*"'
- name: userId
in: query
required: false
schema:
type: string
description: 'Filter on userId: `userId=value` matches it exactly. Bracket operators: `userId[ne]`, `userId[gt]`, `userId[gte]`, `userId[lt]`, `userId[lte]`, `userId[contains]`, `userId[starts]`, `userId[ends]`, and `userId[in]` / `userId[nin]` with comma-separated values.'
- name: name
in: query
required: false
schema:
type: string
description: 'Filter on name: `name=value` matches it exactly. Bracket operators: `name[ne]`, `name[gt]`, `name[gte]`, `name[lt]`, `name[lte]`, `name[contains]`, `name[starts]`, `name[ends]`, and `name[in]` / `name[nin]` with comma-separated values.'
- name: email
in: query
required: false
schema:
type: string
description: 'Filter on email: `email=value` matches it exactly. Bracket operators: `email[ne]`, `email[gt]`, `email[gte]`, `email[lt]`, `email[lte]`, `email[contains]`, `email[starts]`, `email[ends]`, and `email[in]` / `email[nin]` with comma-separated values.'
- name: isPending
in: query
required: false
schema:
type: boolean
description: 'Filter on isPending: `isPending=value` matches it exactly. Bracket operators: `isPending[ne]`, `isPending[gt]`, `isPending[gte]`, `isPending[lt]`, `isPending[lte]`, and `isPending[in]` / `isPending[nin]` with comma-separated values.'
- name: roleId
in: query
required: false
schema:
type: string
description: 'Filter on roleId: `roleId=value` matches it exactly. Bracket operators: `roleId[ne]`, `roleId[gt]`, `roleId[gte]`, `roleId[lt]`, `roleId[lte]`, `roleId[contains]`, `roleId[starts]`, `roleId[ends]`, and `roleId[in]` / `roleId[nin]` with comma-separated values.'
- name: roleName
in: query
required: false
schema:
type: string
description: 'Filter on roleName: `roleName=value` matches it exactly. Bracket operators: `roleName[ne]`, `roleName[gt]`, `roleName[gte]`, `roleName[lt]`, `roleName[lte]`, `roleName[contains]`, `roleName[starts]`, `roleName[ends]`, and `roleName[in]` / `roleName[nin]` with comma-separated values.'
- name: createdOn
in: query
required: false
schema:
type: string
description: 'Filter on createdOn: `createdOn=value` matches it exactly. A date or timestamp, such as 2026-10-01 or 2026-10-01T12:00:00Z. Bracket operators: `createdOn[ne]`, `createdOn[gt]`, `createdOn[gte]`, `createdOn[lt]`, `createdOn[lte]`, and `createdOn[in]` / `createdOn[nin]` with comma-separated values.'
- $ref: '#/components/parameters/CompanyId'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
const: true
data:
type: array
items:
$ref: '#/components/schemas/Member'
meta:
$ref: '#/components/schemas/ListMeta'
required:
- success
- data
- meta
default:
description: Error.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
operationId: v2-invite-member
summary: Invite one or more people to the company by email
description: 'Invite one or more people to the company by email. Omit roleId to place the invitee on the built-in Member role, matching the product invite flow. Emails already in the company keep their existing membership and have their role set to the one given here.
Requires the API key permission member:create.'
tags:
- Members
security:
- apiKey:
- member:create
parameters:
- $ref: '#/components/parameters/CompanyId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
invites:
minItems: 1
maxItems: 50
type: array
items:
type: object
properties:
email:
type: string
format: email
pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
roleId:
type: string
minLength: 1
required:
- email
required:
- invites
responses:
'201':
description: Success.
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
const: true
data:
type: array
items:
$ref: '#/components/schemas/Member'
meta:
type: object
description: 'Response metadata, on endpoints that answer it: the usage window, a step count, or the provider paging of a Universal action run.'
required:
- success
- data
'400':
description: The request body is invalid (INVALID_QUERY), the company id or emails did not reach the invite service (MEMBER_COMPANY_ID_REQUIRED, MEMBER_EMAILS_REQUIRED), or the invites were refused (MEMBER_INVITE_FAILED).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: The key has no resolvable acting user (MEMBER_ACTOR_NOT_RESOLVED), a role grants permissions the acting user does not hold (MEMBER_ROLE_EXCEEDS_ACTOR), an existing Owner would change role without member:owner_edit (MEMBER_ROLE_OWNER_MODIFICATION_FORBIDDEN), or the company is a personal workspace and takes no invites (MEMBER_PERSONAL_COMPANY_INVITES_FORBIDDEN), or a key of the parent organization tried to change an Owner of this child organization (PARENT_DELEGATE_OWNER_FORBIDDEN) or assign a role with a reserved permission (PARENT_DELEGATE_ROLE_FORBIDDEN).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
default:
description: Error.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/v2/members/{userId}:
patch:
operationId: v2-update-member
summary: Set a company member's role
description: 'Set a company member''s role. The key acts as the user who created it: it cannot change that user''s own role, it assigns only a role whose permissions its creator holds and the key holds too, and it changes an Owner only when its creator and the key both hold member:owner_edit. The last Owner cannot be demoted.
Requires the API key permission member:edit.'
tags:
- Members
security:
- apiKey:
- member:edit
parameters:
- name: userId
in: path
required: true
schema:
type: string
minLength: 1
- $ref: '#/components/parameters/CompanyId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
roleId:
type: string
minLength: 1
required:
- roleId
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
const: true
data:
$ref: '#/components/schemas/Member'
meta:
type: object
description: 'Response metadata, on endpoints that answer it: the usage window, a step count, or the provider paging of a Universal action run.'
required:
- success
- data
'400':
description: The request is invalid (INVALID_QUERY, MEMBER_ROLE_UPDATE_INVALID_REQUEST), the role cannot be assigned (MEMBER_ROLE_ASSIGNMENT_INVALID), or the target is the last Owner (MEMBER_ROLE_LAST_OWNER_DEMOTION_FORBIDDEN).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: The key has no resolvable acting user (MEMBER_ACTOR_NOT_RESOLVED), the target is the acting user (MEMBER_ROLE_SELF_UPDATE_FORBIDDEN), or the role grants permissions the acting user does not hold (MEMBER_ROLE_EXCEEDS_ACTOR), or the target is an Owner and the creator or the key lacks member:owner_edit (MEMBER_ROLE_OWNER_MODIFICATION_FORBIDDEN), or a key of the parent organization tried to change an Owner of this child organization (PARENT_DELEGATE_OWNER_FORBIDDEN) or assign a role with a reserved permission (PARENT_DELEGATE_ROLE_FORBIDDEN).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: No member with this user id is in the company (MEMBER_ROLE_UPDATE_NOT_FOUND).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: The role update transaction failed (MEMBER_ROLE_UPDATE_FAILED).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
default:
description: Error.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
delete:
operationId: v2-delete-member
summary: Remove someone from the company
description: 'Remove someone from the company. One route covers both cases: an active member loses access, and a person who was invited but never signed up has the pending invite cancelled. The key acts as the user who created it: it cannot remove that user, and it removes an Owner only when its creator and the key both hold member:owner_edit. The last Owner cannot be removed. Removing a user who is not in the company succeeds and changes nothing.
Requires the API key permission member:delete.'
tags:
- Members
security:
- apiKey:
- member:delete
parameters:
- name: userId
in: path
required: true
schema:
type: string
minLength: 1
- $ref: '#/components/parameters/CompanyId'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
const: true
data:
$ref: '#/components/schemas/DeletedResource'
meta:
type: object
description: 'Response metadata, on endpoints that answer it: the usage window, a step count, or the provider paging of a Universal action run.'
required:
- success
- data
'400':
description: The request is invalid (INVALID_QUERY, MEMBER_DELETE_INVALID_REQUEST), the target is the last Owner (MEMBER_DELETE_LAST_OWNER_FORBIDDEN), or the removal was refused (MEMBER_DELETE_FAILED).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: The key has no resolvable acting user (MEMBER_ACTOR_NOT_RESOLVED), the target is the acting user (MEMBER_DELETE_SELF_FORBIDDEN), or the target is an Owner and the creator or the key lacks member:owner_edit (MEMBER_DELETE_OWNER_FORBIDDEN), or a key of the parent organization tried to change an Owner of this child organization (PARENT_DELEGATE_OWNER_FORBIDDEN).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
default:
description: Error.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
Member:
type: object
properties:
userId:
type:
- string
- 'null'
description: Unique id of the member.
name:
type:
- string
- 'null'
description: The member's name.
email:
type:
- string
- 'null'
description: The member's email address.
isPending:
type: boolean
description: True while their invite is waiting to be accepted.
roleId:
type:
- string
- 'null'
description: The role they hold.
roleName:
type:
- string
- 'null'
description: That role's name.
createdOn:
type:
- string
- 'null'
format: date-time
description: When they joined the company.
required:
- userId
- name
- email
- isPending
- roleId
- roleName
- createdOn
ErrorResponse:
type: object
properties:
success:
type: boolean
const: false
error:
type: object
properties:
code:
type: string
description: 'Fixed by the status: 400 VALIDATION_FAILED, 401 UNAUTHORIZED, 403 FORBIDDEN, 404 NOT_FOUND, 409 CONFLICT, 410 GONE, 413 PAYLOAD_TOO_LARGE, 421 and 503 UNAVAILABLE, 429 RATE_LIMITED, 500 INTERNAL_ERROR, 502 PROVIDER_ERROR.'
examples:
- VALIDATION_FAILED
- UNAUTHORIZED
- FORBIDDEN
- NOT_FOUND
- CONFLICT
- GONE
- PAYLOAD_TOO_LARGE
- RATE_LIMITED
- INTERNAL_ERROR
- PROVIDER_ERROR
- UNAVAILABLE
message:
type: string
details:
type: object
description: The specific cause (reason) and any evidence for it. Absent when the code says everything.
properties:
reason:
type: string
description: The specific cause, for example UNKNOWN_FILTER_FIELD or CONNECTION_IN_USE.
field:
type: string
description: The parameter, filter, sort, or body field a 400 refused.
accepted:
type: array
items: {}
description: 'What a 400 would have accepted instead: parameter names, field names, or enum values.'
provider:
type: object
description: 'On 502 PROVIDER_ERROR: the provider answer that failed.'
properties:
status:
type: integer
description: The provider's HTTP status.
code:
type: string
description: The provider's own error code, when it sends one.
required:
- code
- message
required:
- success
- error
ListMeta:
type: object
properties:
pagination:
type: object
properties:
totalCount:
type: integer
description: Matching records. A list read in SQL stops counting at 10000.
totalCountExact:
type: boolean
description: False when totalCount stopped at that cap.
hasNextPage:
type: boolean
description: True when another page follows. Page with this, not totalCount.
hasPreviousPage:
type: boolean
required:
- totalCount
- totalCountExact
- hasNextPage
- hasPreviousPage
required:
- pagination
DeletedResource:
type: object
properties:
id:
type: string
description: The id of the resource that was deleted.
required:
- id
parameters:
offset:
name: offset
in: query
required: false
schema:
type: integer
minimum: 0
default: 0
description: Number of results to skip for pagination (default 0).
limit:
name: limit
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 1000
default: 100
description: Maximum number of results to return (1-1000, default 100).
CompanyId:
name: X-Company-Id
in: header
required: false
schema:
type: string
example: K3vX9qLm2RtA
description: Optional. The id of a child organization of your API key's company to act on. Leave it out to act on the key's own company. See [Child organizations](https://mindcloud.co/docs/api/rest/introduction/child-organizations).
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'Company-scoped MindCloud API key sent as `Authorization: Bearer <key>`. Each key holds permissions from the MindCloud roles catalog (workflow:view, workflow:run, universal:read, api_key:manage, ...), chosen when it is created; each operation lists the ones it requires as this scheme''s roles. A request may use a permission only while the user who created the key still holds it in the company the request acts on; a missing one answers 403 with details.reason API_KEY_PERMISSION_DENIED and details.required, and a key whose creator left the organization answers API_KEY_CREATOR_INACTIVE. The key''s accessLevel (full_access, run_workflows, read_only, or none) summarizes its permissions. Create a key at https://app.mindcloud.co/user/api-keys.'