Permissions & Search API
This page covers two related resources:
/api/v1/permissionsreads who can do what in your organisation and manages project team membership, with a summary of the advanced permission-administration endpoints./api/v1/searchfinds people by meaning rather than exact keywords, using semantic search over staff profiles.
Authentication: every endpoint requires an API key. See API Keys & Authentication.
All permissions endpoints take your organisation’s ID as the first path
segment. It must be the organisation your key is bound to; other
organisations return 403.
Runnit’s built-in roles, from least to most access, are freelancer,
member, manager, admin, and owner. Organisations can also define
custom roles that adjust what a base role can do.
GET /api/v1/permissions/:orgId/members
Section titled “GET /api/v1/permissions/:orgId/members”List every active member of the organisation with their role, profile details, client access summary, and whether they can update project status.
Managers, admins, and owners can call this.
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/members \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "members": [ { "id": "a6c840eb-...", "userId": "213ae491-...", "organizationId": "ddf75d91-...", "role": "owner", "isPrimary": true, "isFreelancer": false, "hourlyRate": 289.33, "currency": "AUD", "joinedAt": "2026-05-29T20:50:45.017Z", "isActive": true, "user": { "id": "213ae491-...", "email": "mark@example.com", "firstName": "Mark", "lastName": "Tolson", "displayName": "Mark Tolson" }, "hasAllClientAccess": true, "accessibleClientCount": null, "canUpdateProjectStatus": true } ]}hasAllClientAccess is true when the member is not restricted to specific
clients; otherwise accessibleClientCount says how many clients they can
see.
Errors
403if your role is below manager.
GET /api/v1/permissions/:orgId/users/:userId/access
Section titled “GET /api/v1/permissions/:orgId/users/:userId/access”Get one member’s complete access picture: role, effective capability list with where each capability came from, client assignments, and project memberships.
Managers, admins, and owners can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| orgId | UUID | Yes | Your organisation. |
| userId | UUID | Yes | The member to inspect. |
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/users/<user-id>/access \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "user": { "userId": "213ae491-...", "role": "member", "customRoleId": null, "isActive": true, "capabilities": ["project:view", "task:view", "..."], "capabilityProvenance": { "project:view": ["role"] }, "permissions": { "canUpdateProjectStatus": true }, "hasAllClientAccess": false, "clientAssignments": [ { "clientOrganizationId": "b2c3d4e5-...", "clientName": "Northwind", "clientSlug": "northwind" } ], "projects": [ { "id": "1f0e9a52-...", "name": "Winter Launch", "status": "active", "clientName": "Northwind" } ] }}capabilities lists the member’s effective permission identifiers, and
capabilityProvenance records whether each one came from their role, a
custom role, or an individual grant.
Errors
403if your role is below manager.404if the user is not a member of the organisation.
GET /api/v1/permissions/:orgId/me/capabilities
Section titled “GET /api/v1/permissions/:orgId/me/capabilities”Get your own role and effective capability list. Useful for deciding which actions your integration should offer before attempting them.
Any organisation member can call this.
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/me/capabilities \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "role": "manager", "customRoleId": null, "capabilities": ["project:view", "project:create", "..."] }GET /api/v1/permissions/:orgId/role-matrix
Section titled “GET /api/v1/permissions/:orgId/role-matrix”Get the built-in role-to-capability matrix: which capabilities each of the five built-in roles includes.
Any organisation member can call this.
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/role-matrix \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "roles": ["freelancer", "member", "manager", "admin", "owner"], "capabilities": ["project:view", "project:create", "..."], "matrix": { "member": ["project:view", "task:view", "..."] }}GET /api/v1/permissions/:orgId/projects
Section titled “GET /api/v1/permissions/:orgId/projects”List the organisation’s projects (up to 200) with access information: member count, owner, and whether the project’s client restricts who can see it.
Managers, admins, and owners can call this.
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/projects \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "projects": [ { "id": "1f0e9a52-...", "name": "Winter Launch", "status": "active", "clientOrganizationId": "b2c3d4e5-...", "clientName": "Northwind", "memberCount": 4, "clientRestricted": false, "ownerId": "213ae491-..." } ]}Errors
403if your role is below manager.
GET /api/v1/permissions/:orgId/projects/:projectId/members
Section titled “GET /api/v1/permissions/:orgId/projects/:projectId/members”List a project’s team members with their user details, project role title, rates, and allocations.
Any organisation member can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| orgId | UUID | Yes | Your organisation. |
| projectId | UUID | Yes | Project to inspect. |
Example request
curl https://api.runnit.io/api/v1/permissions/<org-id>/projects/<project-id>/members \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "members": [ { "id": "e2f4a6b8-...", "projectId": "1f0e9a52-...", "userId": "d3a6f0c1-...", "isAdmin": false, "roleTitle": "Copywriter", "hourlyRate": 140, "currency": "AUD", "rateCardRoleId": "f7a1c3e5-...", "billableRateSnapshot": 180, "billableCurrencySnapshot": "AUD", "allocatedHours": 40, "maxHoursPerWeek": 20, "joinedAt": "2026-07-01T00:00:00.000Z", "user": { "id": "d3a6f0c1-...", "email": "alex@example.com", "firstName": "Alex", "lastName": "Chen", "displayName": "Alex Chen" } } ]}The rate and allocation fields are absent when they were never set for the member.
POST /api/v1/permissions/:orgId/projects/:projectId/members
Section titled “POST /api/v1/permissions/:orgId/projects/:projectId/members”Add a member to a project’s team, optionally with a role title, rates, and hour allocations.
Managers, admins, and owners can call this.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| userId | UUID | Yes | User to add. |
| isAdmin | boolean | No | Make the member a project admin. Defaults to false. |
| roleTitle | string | No | Role on this project, up to 120 characters. |
| hourlyRate | number | No | Internal hourly rate, 0 or more. |
| currency | string | No | Three-letter currency code, e.g. AUD. |
| rateCardRoleId | UUID | No | Rate card role that drives the billable rate. |
| billableRateSnapshot | number | No | Billable rate captured for this project, 0 or more. |
| billableCurrencySnapshot | string | No | Three-letter currency code for the billable rate. |
| allocatedHours | number | No | Total hours allocated on this project, 0 or more. |
| maxHoursPerWeek | number | No | Weekly cap for this member on this project, 0 or more. |
Example request
curl -X POST https://api.runnit.io/api/v1/permissions/<org-id>/projects/<project-id>/members \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "userId": "<user-id>", "roleTitle": "Copywriter", "allocatedHours": 40 }'Response
{ "success": true, "member": { "projectId": "1f0e9a52-...", "userId": "d3a6f0c1-...", "roleTitle": "Copywriter" } }The addition is also recorded in the project’s history feed.
Errors
400ifuserIdis missing or a field fails validation (negative rates or hours, role title over 120 characters, currency not 3 letters).403if your role is below manager.
PATCH /api/v1/permissions/:orgId/projects/:projectId/members/:userId
Section titled “PATCH /api/v1/permissions/:orgId/projects/:projectId/members/:userId”Update a project member’s assignment. Send only the fields you want to
change; the fields and constraints match the add endpoint (everything except
userId, which moves into the path).
Managers, admins, and owners can call this.
Example request
curl -X PATCH https://api.runnit.io/api/v1/permissions/<org-id>/projects/<project-id>/members/<user-id> \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "roleTitle": "Senior Copywriter", "maxHoursPerWeek": 25 }'Response
{ "success": true, "member": { "userId": "d3a6f0c1-...", "roleTitle": "Senior Copywriter", "maxHoursPerWeek": 25 } }Errors
400if a field fails validation.403if your role is below manager.
DELETE /api/v1/permissions/:orgId/projects/:projectId/members/:userId
Section titled “DELETE /api/v1/permissions/:orgId/projects/:projectId/members/:userId”Remove a member from a project’s team. This does not remove them from the organisation.
Managers, admins, and owners can call this.
Example request
curl -X DELETE https://api.runnit.io/api/v1/permissions/<org-id>/projects/<project-id>/members/<user-id> \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "message": "Member removed from project" }Errors
403if your role is below manager.
Permission administration endpoints
Section titled “Permission administration endpoints”The remaining permissions endpoints administer roles and access rules. They
mirror the Admin area in the app; most integrations only need the read
endpoints above. All of them follow the same { "success": true, ... }
envelope and the organisation binding described at the top of this page.
| Method and path | What it does | Who can call it |
|---|---|---|
PATCH /:orgId/members/:userId/role | Change a member’s built-in role (body: role). Assigning admin or owner requires the owner role. You cannot change your own role unless you are the owner. | Admins and owners |
PATCH /:orgId/members/:userId/project-status-permission | Allow or block a member from updating project status (body: canUpdateProjectStatus, boolean). | Admins and owners |
GET /:orgId/capability-catalog | Capability metadata (labels, groups, descriptions) plus the role matrix, for building admin UI. | Any member |
GET /:orgId/custom-roles | List the organisation’s custom roles. | Custom-role managers |
POST /:orgId/custom-roles | Create a custom role (body: key, name, baseRole, optional description, addedCapabilities, removedCapabilities). Returns 201. | Custom-role managers |
PATCH /:orgId/custom-roles/:roleId | Update a custom role’s name, base role, description, or capability adjustments. | Custom-role managers |
DELETE /:orgId/custom-roles/:roleId | Delete a custom role. | Custom-role managers |
GET /:orgId/role-presets | List the ready-made custom role presets. | Custom-role managers |
POST /:orgId/role-presets/install | Install the system presets as custom roles. | Custom-role managers |
POST /:orgId/members/:userId/custom-role | Assign a custom role to a member (body: customRoleId). | Role managers |
GET /:orgId/members/:userId/grants | List a member’s individual capability grants. | Grant managers |
POST /:orgId/members/:userId/grants | Grant or deny one capability for a member (body: capability, optional effect of allow or deny, scopeType, scopeId, reason, expiresAt). Returns 201. | Grant managers |
DELETE /:orgId/grants/:grantId | Revoke a capability grant. | Grant managers |
GET /:orgId/audit-log | Permission change history, filterable by targetUserId, action, decision, with limit (up to 500) and offset. | Audit viewers |
GET /:orgId/settings and PATCH /:orgId/settings | Read or update organisation-wide permission settings, including the enforcement mode. | Settings managers |
The “who can call it” labels reflect capabilities that admins and owners hold by default and that can be granted to others through custom roles or grants.
GET /api/v1/search/users
Section titled “GET /api/v1/search/users”Search staff profiles with a natural-language query. Results are ranked by semantic similarity, so “someone who can lead a rebrand” matches profiles that never use those exact words.
Any organisation member can call this. Results only include people in the organisation you search.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| q | string | Yes | The search query, in plain language. |
| organizationId | UUID | No | Organisation to search. Defaults to your key’s organisation. Must be an organisation you belong to. |
| limit | integer | No | 1 to 50. Defaults to 10. |
| threshold | number | No | Minimum similarity between 0 and 1. Defaults to 0.5. Lower it for broader matches. |
Example request
curl "https://api.runnit.io/api/v1/search/users?q=creative%20director%20with%20campaign%20experience&limit=5" \ -H 'Authorization: Bearer rnk_your_key'Response
{ "query": "creative director with campaign experience", "count": 1, "threshold": 0.5, "organizationId": "ddf75d91-...", "results": [ { "id": "213ae491-...", "email": "mark@example.com", "displayName": "Mark Tolson", "currentTitle": "Chief Creative Officer", "skills": ["Creative Direction", "Campaign Leadership"], "shortBio": "Chief Creative Officer with 21 years in agency delivery.", "similarity": 0.82 } ]}Each result is a full staff profile plus a similarity score between 0
and 1. Profiles that have not been indexed yet do not appear.
Errors
400ifqis missing, orlimitorthresholdis out of range.403if you request an organisation you do not belong to.
GET /api/v1/search/users/expertise
Section titled “GET /api/v1/search/users/expertise”Find people with specific skills. The skills you list are expanded into an expertise-focused semantic query with a broader match threshold than the general search.
Any organisation member can call this.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| skills | string | Yes | Skills to look for, e.g. motion design, After Effects. |
| organizationId | UUID | No | Organisation to search. Defaults to your key’s organisation. |
| limit | integer | No | 1 to 50. Defaults to 10. |
Example request
curl "https://api.runnit.io/api/v1/search/users/expertise?skills=motion%20design" \ -H 'Authorization: Bearer rnk_your_key'Response
{ "query": "motion design", "searchType": "expertise", "count": 2, "organizationId": "ddf75d91-...", "results": [] }results has the same profile-plus-similarity shape as the general user
search.
Errors
400ifskillsis missing.403if you request an organisation you do not belong to.
GET /api/v1/search/users/projects
Section titled “GET /api/v1/search/users/projects”Find people who have worked on similar projects, optionally narrowed by client or technology.
Any organisation member can call this.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| projectType | string | Yes | The kind of project, e.g. brand campaign. |
| client | string | No | Similar client context. |
| technologies | string | No | Tools or technologies used. |
| organizationId | UUID | No | Organisation to search. Defaults to your key’s organisation. |
| limit | integer | No | 1 to 50. Defaults to 10. |
Example request
curl "https://api.runnit.io/api/v1/search/users/projects?projectType=brand%20campaign&technologies=Figma" \ -H 'Authorization: Bearer rnk_your_key'Response
{ "projectType": "brand campaign", "client": null, "technologies": "Figma", "count": 3, "organizationId": "ddf75d91-...", "results": [] }Errors
400ifprojectTypeis missing.403if you request an organisation you do not belong to.
GET /api/v1/search/users/:userId/similar
Section titled “GET /api/v1/search/users/:userId/similar”Find people whose profiles are similar to a given person, for example to build a team around a proven profile or find cover for someone unavailable. The comparison uses the person’s title, skills, and bio; the person themselves is excluded from the results.
Any organisation member can call this for people who share an organisation with them.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| userId | UUID | Yes | The person to match against. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1 to 20. Defaults to 5. |
Example request
curl "https://api.runnit.io/api/v1/search/users/<user-id>/similar?limit=3" \ -H 'Authorization: Bearer rnk_your_key'Response
{ "userId": "213ae491-...", "count": 1, "results": [ { "id": "d3a6f0c1-...", "displayName": "Alex Chen", "similarity": 0.71 } ] }If the person’s profile has no title, skills, or bio to compare, the endpoint returns an empty result list.
Errors
403if you do not share an organisation with the person.404if the user does not exist.
Next steps
Section titled “Next steps”Manage organisation membership itself (invites, removals, and rates) through the Organisations API, or review report access with the Reports API.