curl --request PUT \
--url https://api.example.com/api/decisions/{workspace}/members/{subject} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"roles": []
}'import requests
url = "https://api.example.com/api/decisions/{workspace}/members/{subject}"
payload = { "roles": [] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({roles: []})
};
fetch('https://api.example.com/api/decisions/{workspace}/members/{subject}', 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://api.example.com/api/decisions/{workspace}/members/{subject}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'roles' => [
]
]),
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://api.example.com/api/decisions/{workspace}/members/{subject}"
payload := strings.NewReader("{\n \"roles\": []\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.example.com/api/decisions/{workspace}/members/{subject}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"roles\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/decisions/{workspace}/members/{subject}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"roles\": []\n}"
response = http.request(request)
puts response.read_body{
"workspace": "<string>",
"role_grants": [
{
"keycloak_role": "<string>",
"roles": [
"workspace_admin"
],
"granted_by": "<string>",
"granted_at": "2023-11-07T05:31:56Z"
}
],
"members": [
{
"subject": "<string>",
"roles": [
"workspace_admin"
],
"granted_by": "<string>",
"granted_at": "2023-11-07T05:31:56Z"
}
]
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}Revoke a user's legacy workspace roles
Revoke a legacy grant made directly to a user.
Send an empty roles list. You can no longer grant workspace roles
to individual users, so a non-empty list returns 422. Grant them to
a Keycloak role with PUT /{workspace}/role-grants/{keycloak_role}
instead. Requires a Keycloak session; PSK credentials can’t change
who has access.
Required roles: workspace_admin or nx1_decisions_admin
curl --request PUT \
--url https://api.example.com/api/decisions/{workspace}/members/{subject} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"roles": []
}'import requests
url = "https://api.example.com/api/decisions/{workspace}/members/{subject}"
payload = { "roles": [] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({roles: []})
};
fetch('https://api.example.com/api/decisions/{workspace}/members/{subject}', 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://api.example.com/api/decisions/{workspace}/members/{subject}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'roles' => [
]
]),
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://api.example.com/api/decisions/{workspace}/members/{subject}"
payload := strings.NewReader("{\n \"roles\": []\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.example.com/api/decisions/{workspace}/members/{subject}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"roles\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/decisions/{workspace}/members/{subject}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"roles\": []\n}"
response = http.request(request)
puts response.read_body{
"workspace": "<string>",
"role_grants": [
{
"keycloak_role": "<string>",
"roles": [
"workspace_admin"
],
"granted_by": "<string>",
"granted_at": "2023-11-07T05:31:56Z"
}
],
"members": [
{
"subject": "<string>",
"roles": [
"workspace_admin"
],
"granted_by": "<string>",
"granted_at": "2023-11-07T05:31:56Z"
}
]
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}{
"error": "<string>",
"code": 500
}Authorizations
The access token received from the authorization server in the OAuth 2.0 flow.
Path Parameters
Workspace slug.
64^[a-z0-9][a-z0-9-]{0,63}$Keycloak username whose legacy grant to revoke.
Body
Complete set of workspace roles for this grantee. Replaces any existing grants — send an empty list to revoke.
Per-workspace role grants.
workspace_admin deliberately cannot approve: separation of duties
is the point of the matrix, and approval requires a distinct
approver identity.
workspace_admin, author, approver, simulator, viewer Response
Successful Response
Workspace roles granted to Keycloak roles. This is how access is granted.
Show child attributes
Show child attributes
Legacy grants made directly to users. Still honoured and revocable, but no new ones can be made — replace them with role grants.
Show child attributes
Show child attributes
Was this page helpful?

