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 `. 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.'