Create a custom role
curl --request POST \
--url https://{tenant}.cubecloud.dev/api/v1/roles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"policies": [
{
"actions": [],
"resources": [
"<string>"
]
}
],
"description": "<string>"
}
'import requests
url = "https://{tenant}.cubecloud.dev/api/v1/roles"
payload = {
"name": "<string>",
"policies": [
{
"actions": [],
"resources": ["<string>"]
}
],
"description": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
policies: [{actions: [], resources: ['<string>']}],
description: '<string>'
})
};
fetch('https://{tenant}.cubecloud.dev/api/v1/roles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{tenant}.cubecloud.dev/api/v1/roles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'policies' => [
[
'actions' => [
],
'resources' => [
'<string>'
]
]
],
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{tenant}.cubecloud.dev/api/v1/roles"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://{tenant}.cubecloud.dev/api/v1/roles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{tenant}.cubecloud.dev/api/v1/roles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"policies": [
{
"actions": [
"All"
],
"resourceType": "Global",
"resources": [
"<string>"
]
}
],
"description": "<string>"
}Roles
Create a custom role
POST
/
api
/
v1
/
roles
Create a custom role
curl --request POST \
--url https://{tenant}.cubecloud.dev/api/v1/roles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"policies": [
{
"actions": [],
"resources": [
"<string>"
]
}
],
"description": "<string>"
}
'import requests
url = "https://{tenant}.cubecloud.dev/api/v1/roles"
payload = {
"name": "<string>",
"policies": [
{
"actions": [],
"resources": ["<string>"]
}
],
"description": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
policies: [{actions: [], resources: ['<string>']}],
description: '<string>'
})
};
fetch('https://{tenant}.cubecloud.dev/api/v1/roles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{tenant}.cubecloud.dev/api/v1/roles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'policies' => [
[
'actions' => [
],
'resources' => [
'<string>'
]
]
],
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{tenant}.cubecloud.dev/api/v1/roles"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://{tenant}.cubecloud.dev/api/v1/roles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://{tenant}.cubecloud.dev/api/v1/roles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"policies\": [\n {\n \"actions\": [],\n \"resources\": [\n \"<string>\"\n ]\n }\n ],\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"policies": [
{
"actions": [
"All"
],
"resourceType": "Global",
"resources": [
"<string>"
]
}
],
"description": "<string>"
}🔒 Admin only. Requires administrator privileges — the authenticated principal (API key, embed JWT, or any bearer token) must belong to a user with the admin role.
Creates a role and returns it with the
The tier is not required and is not derived from the deployment actions: a role sent without one is accepted and simply confers no tier, so a user holding only that role gets the deployment grants and still cannot use the product. That is deliberate — a user holds the union of their roles, so a role that only adds a deployment scope beside another role carrying the tier is valid — but it means nothing warns you. Omit the
id to assign it by. Role names are unique, so recreating one that already exists answers 409.
To scope the role to specific deployments, send a Deployment policy whose resources are the deployment ids as strings. A role usually also needs a Global policy naming the holder’s license tier — AIBIView, AIBIExplore or AIBIDevelop — which is what the console calls the role’s Base Role. Pick a tier that covers the deployment actions you grant: the console forces Developer (AIBIDevelop) for every deployment action except DeploymentRead, DeploymentAgentRead, DownloadData and ChatHistoryRead, so a lower tier here produces a role its role builder would not have let you save:
{
"name": "team-analytics",
"policies": [
{
"resourceType": "Global",
"actions": ["AIBIDevelop"],
"resources": ["All"]
},
{
"resourceType": "Deployment",
"actions": ["DeploymentRead", "SchemaRead", "SqlRunnerRead"],
"resources": ["1734"]
}
]
}
Global policy only when some other role supplies the tier. Note the console does require one on every role it writes, so a role created here without a tier shows an unset Base Role when opened in the role builder.
Use ["All"] as resources to cover every deployment, and "All" as the single action for full access to the deployments in scope. A policy is rejected when it grants no action, when it names no resource, or when a Deployment resource is not a deployment id — pass the numeric id returned by POST /build/api/v1/deployments, not a slug or a name. Ids are checked for shape, not existence, so an id whose deployment was since deleted is kept and simply grants nothing.Authorizations
Token authentication. Send Authorization: Bearer <YOUR_TOKEN>.
Body
application/json
RoleCreateBody
Unique role name. Cannot be one of the built-in role names, which are rejected with 400: Admin, Developer, AIBIDeveloper, D3User, AIBIUser, AIBIViewer, Guest, EmbedUser. (The console additionally refuses None and All in its own role builder; those are accepted here.)
Required string length:
1 - 255What the role grants. Scope a policy to specific deployments by setting resourceType to Deployment and listing the deployment ids — as returned by POST /build/api/v1/deployments — in resources, or ["All"] for every deployment. An empty policies list is accepted and creates a role that grants nothing yet; an individual policy with no actions, or none naming a resource, is rejected.
Maximum array length:
200Show child attributes
Show child attributes
Maximum string length:
255