Public API: Roles

A role is a named bundle of permissions you assign to members. Every org ships with five built-in roles (Owner, Admin, Developer, Collaborator, Viewer) and may define up to ten custom roles. Each role carries ten boolean permission flags (e.g. createExperiments, publishProduction, viewReports).

Roles are organization-scoped, so this API is org-level: the path carries no {orgId}, since the org is taken from your service token.

Plain text
https://app.avsb.cloud/api/v1/roles
Plain text1 line

Every endpoint here follows the shared conventions: the { data } envelope, an Idempotency-Key on every write below, and the standard error shape. Every /api/v1 token is rate-limited the same way everywhere: a scoped token gets 600 reads and 120 writes per minute, and an admin:* token gets 600 requests per minute, reads and writes together. Every endpoint below also needs two plan features at once: Integrations and API access, and Enterprise pack. Missing either one fails the call with 403 feature_disabled, even with a fully-permissioned token. See rate-limit headers and Refusals at 403.

Scopes are the only gate here

The AvsB dashboard limits role management to organization admins. This API does not. A token carrying roles:write can create a custom role with every permission flag turned on, or change what an existing custom role is allowed to do. The Owner role itself can't be edited or deleted through this API (see below). But who holds the Owner role is a different action: it happens over the Members API instead, which has its own, separate protection. Keep roles:write off any token that should not change what other members are allowed to do.

Scopes

A scope is a named permission on your token. It decides exactly what that token is allowed to read or change.

OperationScope
List roles, get one roleroles:read
Create, update, delete a roleroles:write

The permission flags

Every role sets these ten booleans:

manageProjects, editProjectSettings, createExperiments, editCodeVariations, publishProduction, viewReports, exportData, manageSupport, manageIntegrations, viewFlagDrafts.

List roles

GET /api/v1/roles: every role in the org, built-in roles first then custom roles oldest-first. Cursor-paginated (?limit= up to 100, defaulting to 20; ?cursor=).

curl "https://app.avsb.cloud/api/v1/roles?limit=20" \  -H "Authorization: Bearer avsb_svc_..."
Shell2 lines
Response
{  "data": [    {      "id": "<roleId>",      "orgId": "<orgId>",      "name": "Analyst",      "description": "",      "isBuiltIn": false,      "icon": null,      "createdAt": "2026-06-16T00:00:00.000Z",      "manageProjects": false,      "editProjectSettings": false,      "createExperiments": true,      "editCodeVariations": false,      "publishProduction": false,      "viewReports": true,      "exportData": false,      "manageSupport": false,      "manageIntegrations": false,      "viewFlagDrafts": true    }  ],  "page": { "nextCursor": null, "hasMore": false }}
JSON24 lines
400, cursor is malformed
{  "error": {    "code": "pagination_invalid",    "message": "cursor is malformed",    "docUrl": "https://docs.avsb.cloud/docs/developer-reference/public-api/conventions#cursor-pagination",    "requestId": "req_9f2c41ab7e0b4d1e8c35a6f0d2b91e77"  }}
JSON8 lines

Create a role

POST /api/v1/roles: create a custom role. name and all ten permission flags are required; description and icon are optional. name must be 2 to 30 characters. An org may hold at most ten custom roles (else 400).

The smallest working request sends just the name and the ten flags:

curl -X POST https://app.avsb.cloud/api/v1/roles \  -H "Authorization: Bearer avsb_svc_..." \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "name": "Analyst",    "manageProjects": false,    "editProjectSettings": false,    "createExperiments": true,    "editCodeVariations": false,    "publishProduction": false,    "viewReports": true,    "exportData": false,    "manageSupport": false,    "manageIntegrations": false,    "viewFlagDrafts": true  }'
Shell17 lines

Add description and icon to that same body so the role is easy to recognise in the dashboard. The response always returns every field:

Response, with description and icon added to the request
{  "data": {    "id": "<roleId>",    "orgId": "<orgId>",    "name": "Analyst",    "description": "Reads reports, cannot change anything live",    "isBuiltIn": false,    "icon": "chart-bar",    "createdAt": "2026-06-16T00:00:00.000Z",    "manageProjects": false,    "editProjectSettings": false,    "createExperiments": true,    "editCodeVariations": false,    "publishProduction": false,    "viewReports": true,    "exportData": false,    "manageSupport": false,    "manageIntegrations": false,    "viewFlagDrafts": true  }}
JSON21 lines
400, already at the custom-role limit
{  "error": {    "code": "validation_failed",    "message": "Maximum of 10 custom roles per organization.",    "docUrl": "https://docs.avsb.cloud/docs/developer-reference/public-api/conventions#validation-errors",    "requestId": "req_9f2c41ab7e0b4d1e8c35a6f0d2b91e77"  }}
JSON8 lines

Get a role

GET /api/v1/roles/{roleId}: one role by id. A role outside your org returns 404.

curl https://app.avsb.cloud/api/v1/roles/<roleId> \  -H "Authorization: Bearer avsb_svc_..."
Shell2 lines

Returns the same shape as one item in the list above.

404, role not found
{  "error": {    "code": "not_found",    "message": "Role not found",    "docUrl": "https://docs.avsb.cloud/docs/developer-reference/public-api/conventions#not-found-errors",    "requestId": "req_9f2c41ab7e0b4d1e8c35a6f0d2b91e77"  }}
JSON8 lines

Update a role

PATCH /api/v1/roles/{roleId}: partial update of permission flags (and, for custom roles, name / description / icon). Built-in roles accept permission changes only; renaming a built-in role returns 400. The Owner role cannot be modified (403).

curl -X PATCH https://app.avsb.cloud/api/v1/roles/<roleId> \  -H "Authorization: Bearer avsb_svc_..." \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{ "publishProduction": true }'
Shell5 lines

Returns the full role, the same shape as Create's response, with the changed field updated (publishProduction: true here).

Info

Renaming a built-in role returns 400. A role outside your org returns 404, the same shape as Get a role above. Roles do not support conditional writes: a role read sends no ETag, and role updates are last-write-wins. See ETag / If-Match.

403, the Owner role cannot be modified
{  "error": {    "code": "forbidden",    "message": "Cannot modify Owner role",    "docUrl": "https://docs.avsb.cloud/docs/developer-reference/public-api/conventions#refusals-at-403",    "requestId": "req_9f2c41ab7e0b4d1e8c35a6f0d2b91e77"  }}
JSON8 lines

Delete a role

DELETE /api/v1/roles/{roleId}: delete a custom role. Members and pending invitations holding it are reassigned to the Viewer role. Built-in roles cannot be deleted (403).

curl -X DELETE https://app.avsb.cloud/api/v1/roles/<roleId> \  -H "Authorization: Bearer avsb_svc_..." \  -H "Idempotency-Key: $(uuidgen)"
Shell3 lines
Response
{  "data": { "id": "<roleId>", "deleted": true }}
JSON3 lines
403, cannot delete a built-in role
{  "error": {    "code": "forbidden",    "message": "Cannot delete built-in roles",    "docUrl": "https://docs.avsb.cloud/docs/developer-reference/public-api/conventions#refusals-at-403",    "requestId": "req_9f2c41ab7e0b4d1e8c35a6f0d2b91e77"  }}
JSON8 lines

Next steps

Was this helpful?