> This page is for Developers.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.emnify.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.emnify.com/_mcp/server.

# Manage roles and permissions

> Discover available permissions, create and update custom roles, and assign roles to users via the API.

Most endpoints of the emnify REST API require a specific [permission](/developers/api-guidelines/permissions), which is a pair of a *resource* and an *action*.
A few require none, such as authentication.
The three system roles (Administrator, User, and Observer) cover common access patterns, but they can't be edited.
To grant an exact set of permissions, create a *custom role* and assign it to users.

This guide shows how to manage roles and permissions via the API:

* [List the permissions](/developers/api/permission-and-role-management/list-organisation-permissions) available to your Workspace.
* [Create a custom role](/developers/api/permission-and-role-management/create-custom-role).
* [Update the permissions](/developers/api/permission-and-role-management/update-custom-role) of a custom role.
* [Assign a role to a user](/developers/api/user-management/user-role-by-id-and-role-id-put) or remove it.
* [Check your own permissions](/developers/api/permission-and-role-management/get-user-permissions).

## Before you begin

All requests require an [authentication token](/developers/auth/application-tokens).
Managing roles and users requires a role that includes the user and role management permissions, such as Administrator.

In these requests, `orgId` accepts either a numeric Workspace ID or the literal `my`, which targets your own Workspace.
Use a numeric ID when you administer a child Workspace.

## List available permissions

To see every permission you can assign, request the permission catalog:

### Request

GET [https://cdn.emnify.net/api/v1/organisation/\{orgId}/permission](https://cdn.emnify.net/api/v1/organisation/\{orgId}/permission)

**`Permission Catalog`**

```curl Permission Catalog
curl https://cdn.emnify.net/api/v1/organisation/my/permission \
     -H "Authorization: Bearer <token>"
```

**`Permission Catalog`**

```python Permission Catalog
import requests

url = "https://cdn.emnify.net/api/v1/organisation/my/permission"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

**`Permission Catalog`**

```javascript Permission Catalog
const url = 'https://cdn.emnify.net/api/v1/organisation/my/permission';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Permission Catalog`**

```go Permission Catalog
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/organisation/my/permission"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Permission Catalog`**

```ruby Permission Catalog
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/organisation/my/permission")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

**`Permission Catalog`**

```java Permission Catalog
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://cdn.emnify.net/api/v1/organisation/my/permission")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Permission Catalog`**

```php Permission Catalog
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://cdn.emnify.net/api/v1/organisation/my/permission', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

**`Permission Catalog`**

```csharp Permission Catalog
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/organisation/my/permission");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Permission Catalog`**

```swift Permission Catalog
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/organisation/my/permission")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

Each entry pairs a `resource` with an `action` and lists the API routes that the permission guards under `entrypoints`:

### Response (200)

```json
[
  {
    "id": 201,
    "resource": "Endpoint",
    "action": "List",
    "created_at": "2026-01-10T08:00:00.000Z",
    "updated_at": "2026-06-20T14:30:00.000Z",
    "deprecated_at": null,
    "entrypoints": {
      "rest": [
        {
          "method": "GET",
          "path": "/api/v1/endpoint",
          "created_at": "2026-01-10T08:00:00.000Z",
          "updated_at": "2026-01-10T08:00:00.000Z",
          "deprecated_at": null
        }
      ],
      "graphql": [
        {
          "type": "query",
          "path": "endpoints",
          "created_at": "2026-01-10T08:00:00.000Z",
          "updated_at": "2026-01-10T08:00:00.000Z",
          "deprecated_at": null
        }
      ]
    }
  },
  {
    "id": 202,
    "resource": "LegacyReport",
    "action": "Get",
    "created_at": "2025-11-01T00:00:00.000Z",
    "updated_at": "2026-05-01T00:00:00.000Z",
    "deprecated_at": "2026-09-01T00:00:00.000Z",
    "entrypoints": {
      "rest": [],
      "graphql": []
    }
  },
  {
    "id": 203,
    "resource": "Organisation.Permission",
    "action": "List",
    "created_at": "2026-02-15T00:00:00.000Z",
    "updated_at": "2026-02-15T00:00:00.000Z",
    "deprecated_at": null,
    "entrypoints": {
      "rest": [
        {
          "method": "GET",
          "path": "/api/v1/organisation/:orgId/permission",
          "created_at": "2026-02-15T00:00:00.000Z",
          "updated_at": "2026-02-15T00:00:00.000Z",
          "deprecated_at": null
        }
      ],
      "graphql": []
    }
  }
]
```

A permission with a non-null `deprecated_at` timestamp is end-of-life.
It still appears in the catalog so that existing roles keep working, but you can't add it to new or updated roles.

> **Tip**
>
> Every endpoint's reference page shows its required permission in a Required permissions callout.
> Select **Copy JSON** in the callout to copy the permission pair as JSON, ready to paste into the `permissions` array used below.

## Create a custom role

> **Note**
>
> During the beta, a Workspace can hold at most 3 custom roles.
> To request beta access, contact your Customer Success Manager.

Create a role by giving it a unique name and the set of permissions it grants.
List only what you want to grant: anything you leave out is denied.

### Request

POST [https://cdn.emnify.net/api/v1/organisation/\{orgId}/role](https://cdn.emnify.net/api/v1/organisation/\{orgId}/role)

**`Procurement`**

```curl Procurement
curl -X POST https://cdn.emnify.net/api/v1/organisation/orgId/role \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "name": "Procurement",
  "description": "Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.",
  "permissions": [
    {
      "resource": "Sim",
      "action": "List"
    },
    {
      "resource": "Sim.Event",
      "action": "List"
    },
    {
      "resource": "Event.Type",
      "action": "List"
    },
    {
      "resource": "Operator",
      "action": "List"
    },
    {
      "resource": "SimBatch.Bic",
      "action": "Get"
    },
    {
      "resource": "SimBatch.Bic",
      "action": "Add"
    },
    {
      "resource": "Endpoint",
      "action": "Create"
    },
    {
      "resource": "Organisation.Billing.Settings",
      "action": "Get"
    },
    {
      "resource": "TariffProfile",
      "action": "List"
    },
    {
      "resource": "ServiceProfile",
      "action": "List"
    },
    {
      "resource": "IpAddressSpace",
      "action": "List"
    },
    {
      "resource": "Tag",
      "action": "List"
    },
    {
      "resource": "Shop.Order",
      "action": "Create"
    },
    {
      "resource": "Shop.Order.Calculation",
      "action": "Get"
    },
    {
      "resource": "Shop.Product",
      "action": "List"
    },
    {
      "resource": "PaymentOption",
      "action": "List"
    },
    {
      "resource": "ShippingOption.Country",
      "action": "List"
    },
    {
      "resource": "Organisation.Contact",
      "action": "List"
    },
    {
      "resource": "Shop.Order",
      "action": "List"
    },
    {
      "resource": "Shop.Order.Invoice",
      "action": "Get"
    },
    {
      "resource": "Shop.Order.Item.Artifact",
      "action": "Get"
    }
  ]
}'
```

**`Procurement`**

```python Procurement
import requests

url = "https://cdn.emnify.net/api/v1/organisation/:orgId/role"

payload = {
    "name": "Procurement",
    "description": "Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.",
    "permissions": [
        {
            "resource": "Sim",
            "action": "List"
        },
        {
            "resource": "Sim.Event",
            "action": "List"
        },
        {
            "resource": "Event.Type",
            "action": "List"
        },
        {
            "resource": "Operator",
            "action": "List"
        },
        {
            "resource": "SimBatch.Bic",
            "action": "Get"
        },
        {
            "resource": "SimBatch.Bic",
            "action": "Add"
        },
        {
            "resource": "Endpoint",
            "action": "Create"
        },
        {
            "resource": "Organisation.Billing.Settings",
            "action": "Get"
        },
        {
            "resource": "TariffProfile",
            "action": "List"
        },
        {
            "resource": "ServiceProfile",
            "action": "List"
        },
        {
            "resource": "IpAddressSpace",
            "action": "List"
        },
        {
            "resource": "Tag",
            "action": "List"
        },
        {
            "resource": "Shop.Order",
            "action": "Create"
        },
        {
            "resource": "Shop.Order.Calculation",
            "action": "Get"
        },
        {
            "resource": "Shop.Product",
            "action": "List"
        },
        {
            "resource": "PaymentOption",
            "action": "List"
        },
        {
            "resource": "ShippingOption.Country",
            "action": "List"
        },
        {
            "resource": "Organisation.Contact",
            "action": "List"
        },
        {
            "resource": "Shop.Order",
            "action": "List"
        },
        {
            "resource": "Shop.Order.Invoice",
            "action": "Get"
        },
        {
            "resource": "Shop.Order.Item.Artifact",
            "action": "Get"
        }
    ]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

**`Procurement`**

```javascript Procurement
const url = 'https://cdn.emnify.net/api/v1/organisation/:orgId/role';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"name":"Procurement","description":"Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.","permissions":[{"resource":"Sim","action":"List"},{"resource":"Sim.Event","action":"List"},{"resource":"Event.Type","action":"List"},{"resource":"Operator","action":"List"},{"resource":"SimBatch.Bic","action":"Get"},{"resource":"SimBatch.Bic","action":"Add"},{"resource":"Endpoint","action":"Create"},{"resource":"Organisation.Billing.Settings","action":"Get"},{"resource":"TariffProfile","action":"List"},{"resource":"ServiceProfile","action":"List"},{"resource":"IpAddressSpace","action":"List"},{"resource":"Tag","action":"List"},{"resource":"Shop.Order","action":"Create"},{"resource":"Shop.Order.Calculation","action":"Get"},{"resource":"Shop.Product","action":"List"},{"resource":"PaymentOption","action":"List"},{"resource":"ShippingOption.Country","action":"List"},{"resource":"Organisation.Contact","action":"List"},{"resource":"Shop.Order","action":"List"},{"resource":"Shop.Order.Invoice","action":"Get"},{"resource":"Shop.Order.Item.Artifact","action":"Get"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Procurement`**

```go Procurement
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/organisation/:orgId/role"

	payload := strings.NewReader("{\n  \"name\": \"Procurement\",\n  \"description\": \"Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.\",\n  \"permissions\": [\n    {\n      \"resource\": \"Sim\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Sim.Event\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Event.Type\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Operator\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Add\"\n    },\n    {\n      \"resource\": \"Endpoint\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Organisation.Billing.Settings\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"TariffProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ServiceProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"IpAddressSpace\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Tag\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Shop.Order.Calculation\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Product\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"PaymentOption\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ShippingOption.Country\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Organisation.Contact\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order.Invoice\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Order.Item.Artifact\",\n      \"action\": \"Get\"\n    }\n  ]\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(res)
	fmt.Println(string(body))

}
```

**`Procurement`**

```ruby Procurement
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/organisation/:orgId/role")

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\": \"Procurement\",\n  \"description\": \"Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.\",\n  \"permissions\": [\n    {\n      \"resource\": \"Sim\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Sim.Event\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Event.Type\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Operator\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Add\"\n    },\n    {\n      \"resource\": \"Endpoint\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Organisation.Billing.Settings\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"TariffProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ServiceProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"IpAddressSpace\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Tag\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Shop.Order.Calculation\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Product\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"PaymentOption\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ShippingOption.Country\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Organisation.Contact\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order.Invoice\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Order.Item.Artifact\",\n      \"action\": \"Get\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

**`Procurement`**

```java Procurement
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://cdn.emnify.net/api/v1/organisation/:orgId/role")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"name\": \"Procurement\",\n  \"description\": \"Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.\",\n  \"permissions\": [\n    {\n      \"resource\": \"Sim\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Sim.Event\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Event.Type\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Operator\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Add\"\n    },\n    {\n      \"resource\": \"Endpoint\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Organisation.Billing.Settings\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"TariffProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ServiceProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"IpAddressSpace\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Tag\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Shop.Order.Calculation\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Product\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"PaymentOption\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ShippingOption.Country\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Organisation.Contact\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order.Invoice\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Order.Item.Artifact\",\n      \"action\": \"Get\"\n    }\n  ]\n}")
  .asString();
```

**`Procurement`**

```php Procurement
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://cdn.emnify.net/api/v1/organisation/:orgId/role', [
  'body' => '{
  "name": "Procurement",
  "description": "Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.",
  "permissions": [
    {
      "resource": "Sim",
      "action": "List"
    },
    {
      "resource": "Sim.Event",
      "action": "List"
    },
    {
      "resource": "Event.Type",
      "action": "List"
    },
    {
      "resource": "Operator",
      "action": "List"
    },
    {
      "resource": "SimBatch.Bic",
      "action": "Get"
    },
    {
      "resource": "SimBatch.Bic",
      "action": "Add"
    },
    {
      "resource": "Endpoint",
      "action": "Create"
    },
    {
      "resource": "Organisation.Billing.Settings",
      "action": "Get"
    },
    {
      "resource": "TariffProfile",
      "action": "List"
    },
    {
      "resource": "ServiceProfile",
      "action": "List"
    },
    {
      "resource": "IpAddressSpace",
      "action": "List"
    },
    {
      "resource": "Tag",
      "action": "List"
    },
    {
      "resource": "Shop.Order",
      "action": "Create"
    },
    {
      "resource": "Shop.Order.Calculation",
      "action": "Get"
    },
    {
      "resource": "Shop.Product",
      "action": "List"
    },
    {
      "resource": "PaymentOption",
      "action": "List"
    },
    {
      "resource": "ShippingOption.Country",
      "action": "List"
    },
    {
      "resource": "Organisation.Contact",
      "action": "List"
    },
    {
      "resource": "Shop.Order",
      "action": "List"
    },
    {
      "resource": "Shop.Order.Invoice",
      "action": "Get"
    },
    {
      "resource": "Shop.Order.Item.Artifact",
      "action": "Get"
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

**`Procurement`**

```csharp Procurement
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/organisation/:orgId/role");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"Procurement\",\n  \"description\": \"Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.\",\n  \"permissions\": [\n    {\n      \"resource\": \"Sim\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Sim.Event\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Event.Type\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Operator\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"SimBatch.Bic\",\n      \"action\": \"Add\"\n    },\n    {\n      \"resource\": \"Endpoint\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Organisation.Billing.Settings\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"TariffProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ServiceProfile\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"IpAddressSpace\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Tag\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"Create\"\n    },\n    {\n      \"resource\": \"Shop.Order.Calculation\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Product\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"PaymentOption\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"ShippingOption.Country\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Organisation.Contact\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order\",\n      \"action\": \"List\"\n    },\n    {\n      \"resource\": \"Shop.Order.Invoice\",\n      \"action\": \"Get\"\n    },\n    {\n      \"resource\": \"Shop.Order.Item.Artifact\",\n      \"action\": \"Get\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Procurement`**

```swift Procurement
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "name": "Procurement",
  "description": "Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.",
  "permissions": [
    [
      "resource": "Sim",
      "action": "List"
    ],
    [
      "resource": "Sim.Event",
      "action": "List"
    ],
    [
      "resource": "Event.Type",
      "action": "List"
    ],
    [
      "resource": "Operator",
      "action": "List"
    ],
    [
      "resource": "SimBatch.Bic",
      "action": "Get"
    ],
    [
      "resource": "SimBatch.Bic",
      "action": "Add"
    ],
    [
      "resource": "Endpoint",
      "action": "Create"
    ],
    [
      "resource": "Organisation.Billing.Settings",
      "action": "Get"
    ],
    [
      "resource": "TariffProfile",
      "action": "List"
    ],
    [
      "resource": "ServiceProfile",
      "action": "List"
    ],
    [
      "resource": "IpAddressSpace",
      "action": "List"
    ],
    [
      "resource": "Tag",
      "action": "List"
    ],
    [
      "resource": "Shop.Order",
      "action": "Create"
    ],
    [
      "resource": "Shop.Order.Calculation",
      "action": "Get"
    ],
    [
      "resource": "Shop.Product",
      "action": "List"
    ],
    [
      "resource": "PaymentOption",
      "action": "List"
    ],
    [
      "resource": "ShippingOption.Country",
      "action": "List"
    ],
    [
      "resource": "Organisation.Contact",
      "action": "List"
    ],
    [
      "resource": "Shop.Order",
      "action": "List"
    ],
    [
      "resource": "Shop.Order.Invoice",
      "action": "Get"
    ],
    [
      "resource": "Shop.Order.Item.Artifact",
      "action": "Get"
    ]
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/organisation/:orgId/role")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

The response is `201 Created` with the new role and its resolved permission list:

### Response (201)

```json
{
  "id": 102,
  "name": "Procurement",
  "name_translation_key": null,
  "description": "Orders SIMs and registers them into the Workspace. No device operations, reports, or billing.",
  "description_translation_key": null,
  "user_count": 0,
  "type": "custom",
  "is_system_role": false
}
```

Instead of resource and action pairs, you can pass numeric permission IDs from the catalog as `permission_ids`.
The two fields are mutually exclusive: supplying both returns `400 Bad Request`.

Constraints:

* Role names must be unique within the Workspace and 2 to 50 characters.
* Role names accept letters, digits, spaces, and `_`, `-`, `&`, `'`, `(`, and `)`. Any other character returns `400 Bad Request`.
* A Workspace can have at most 50 custom roles.

## Update a role's permissions

Update a custom role with a partial `PATCH`: only the fields you send change.

> **Warning**
>
> Sending `permissions` (or `permission_ids`) replaces the role's existing permission list.
> Include every permission the role should keep, not only the ones you're adding.

### Request

PATCH [https://cdn.emnify.net/api/v1/organisation/\{orgId}/role/\{roleId}](https://cdn.emnify.net/api/v1/organisation/\{orgId}/role/\{roleId})

**`Permission and role management_updateCustomRole_example`**

```curl Permission and role management_updateCustomRole_example
curl -X PATCH https://cdn.emnify.net/api/v1/organisation/my/role/12 \
     -H "Authorization: Bearer <token>"
```

**`Permission and role management_updateCustomRole_example`**

```python Permission and role management_updateCustomRole_example
import requests

url = "https://cdn.emnify.net/api/v1/organisation/my/role/12"

headers = {"Authorization": "Bearer <token>"}

response = requests.patch(url, headers=headers)

print(response.json())
```

**`Permission and role management_updateCustomRole_example`**

```javascript Permission and role management_updateCustomRole_example
const url = 'https://cdn.emnify.net/api/v1/organisation/my/role/12';
const options = {method: 'PATCH', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Permission and role management_updateCustomRole_example`**

```go Permission and role management_updateCustomRole_example
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/organisation/my/role/12"

	req, _ := http.NewRequest("PATCH", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Permission and role management_updateCustomRole_example`**

```ruby Permission and role management_updateCustomRole_example
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/organisation/my/role/12")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

**`Permission and role management_updateCustomRole_example`**

```java Permission and role management_updateCustomRole_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://cdn.emnify.net/api/v1/organisation/my/role/12")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Permission and role management_updateCustomRole_example`**

```php Permission and role management_updateCustomRole_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://cdn.emnify.net/api/v1/organisation/my/role/12', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

**`Permission and role management_updateCustomRole_example`**

```csharp Permission and role management_updateCustomRole_example
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/organisation/my/role/12");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Permission and role management_updateCustomRole_example`**

```swift Permission and role management_updateCustomRole_example
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/organisation/my/role/12")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Response (200)

```json
{
  "id": 102,
  "name": "Fleet operator v2",
  "name_translation_key": null,
  "description": "Updated scope after the Q3 review.",
  "description_translation_key": null,
  "user_count": 2,
  "type": "custom",
  "is_system_role": false,
  "permissions": [
    {
      "id": 2,
      "resource": "Endpoint",
      "action": "List"
    },
    {
      "id": 3,
      "resource": "Endpoint",
      "action": "Create"
    },
    {
      "id": 4,
      "resource": "Endpoint",
      "action": "Delete"
    },
    {
      "id": 5,
      "resource": "ServicePolicy",
      "action": "List"
    }
  ]
}
```

System roles can't be modified or deleted.
To retire a custom role, delete it once no users are assigned to it:

### Request

DELETE [https://cdn.emnify.net/api/v1/organisation/\{orgId}/role/\{roleId}](https://cdn.emnify.net/api/v1/organisation/\{orgId}/role/\{roleId})

```curl
curl -X DELETE https://cdn.emnify.net/api/v1/organisation/my/role/12 \
     -H "Authorization: Bearer <token>"
```

```python
import requests

url = "https://cdn.emnify.net/api/v1/organisation/my/role/12"

headers = {"Authorization": "Bearer <token>"}

response = requests.delete(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://cdn.emnify.net/api/v1/organisation/my/role/12';
const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/organisation/my/role/12"

	req, _ := http.NewRequest("DELETE", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/organisation/my/role/12")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Delete.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.delete("https://cdn.emnify.net/api/v1/organisation/my/role/12")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', 'https://cdn.emnify.net/api/v1/organisation/my/role/12', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/organisation/my/role/12");
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/organisation/my/role/12")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "DELETE"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

## Assign a role to a user

List your roles with their IDs first, if you don't know them:

### Request

GET [https://cdn.emnify.net/api/v1/organisation/\{orgId}/role](https://cdn.emnify.net/api/v1/organisation/\{orgId}/role)

**`Role List`**

```curl Role List
curl https://cdn.emnify.net/api/v1/organisation/my/role \
     -H "Authorization: Bearer <token>"
```

**`Role List`**

```python Role List
import requests

url = "https://cdn.emnify.net/api/v1/organisation/my/role"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

**`Role List`**

```javascript Role List
const url = 'https://cdn.emnify.net/api/v1/organisation/my/role';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Role List`**

```go Role List
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/organisation/my/role"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Role List`**

```ruby Role List
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/organisation/my/role")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

**`Role List`**

```java Role List
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://cdn.emnify.net/api/v1/organisation/my/role")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Role List`**

```php Role List
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://cdn.emnify.net/api/v1/organisation/my/role', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

**`Role List`**

```csharp Role List
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/organisation/my/role");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Role List`**

```swift Role List
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/organisation/my/role")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Response (200)

```json
[
  {
    "id": 1,
    "name": "Administrator",
    "name_translation_key": "user_role_administrator_name",
    "description": "Full access to all organization features and settings.",
    "description_translation_key": "user_role_administrator_description",
    "user_count": 4,
    "type": "system",
    "is_system_role": true
  },
  {
    "id": 2,
    "name": "User",
    "name_translation_key": "user_role_user_name",
    "description": "Partial write access.",
    "description_translation_key": "user_role_user_description",
    "user_count": 12,
    "type": "system",
    "is_system_role": true
  },
  {
    "id": 3,
    "name": "Observer",
    "name_translation_key": "user_role_observer_name",
    "description": "Read-only access.",
    "description_translation_key": "user_role_observer_description",
    "user_count": 3,
    "type": "system",
    "is_system_role": true
  },
  {
    "id": 101,
    "name": "Fleet operator",
    "name_translation_key": null,
    "description": "Can manage endpoints and SIMs but not billing.",
    "description_translation_key": null,
    "user_count": 2,
    "type": "custom",
    "is_system_role": false
  }
]
```

Then assign a role to a user by ID:

### Request

PUT [https://cdn.emnify.net/api/v1/user/\{user_id}/role/\{role_id}](https://cdn.emnify.net/api/v1/user/\{user_id}/role/\{role_id})

```curl
curl -X PUT https://cdn.emnify.net/api/v1/user/1.1/role/1.1 \
     -H "Authorization: Bearer <token>"
```

```python
import requests

url = "https://cdn.emnify.net/api/v1/user/1.1/role/1.1"

headers = {"Authorization": "Bearer <token>"}

response = requests.put(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://cdn.emnify.net/api/v1/user/1.1/role/1.1';
const options = {method: 'PUT', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/user/1.1/role/1.1"

	req, _ := http.NewRequest("PUT", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/user/1.1/role/1.1")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.put("https://cdn.emnify.net/api/v1/user/1.1/role/1.1")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', 'https://cdn.emnify.net/api/v1/user/1.1/role/1.1', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/user/1.1/role/1.1");
var request = new RestRequest(Method.PUT);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/user/1.1/role/1.1")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A successful assignment returns `204 No Content`.
To remove a role from a user, send `DELETE` to the same path.
A user always keeps at least one role: removing the last remaining role returns `409 Conflict`.

## Check your own permissions

To verify what your token can do, list the permissions granted by all of your roles in the current Workspace:

### Request

GET [https://cdn.emnify.net/api/v1/user/my/permission](https://cdn.emnify.net/api/v1/user/my/permission)

**`Your Permissions`**

```curl Your Permissions
curl https://cdn.emnify.net/api/v1/user/my/permission \
     -H "Authorization: Bearer <token>"
```

**`Your Permissions`**

```python Your Permissions
import requests

url = "https://cdn.emnify.net/api/v1/user/my/permission"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

**`Your Permissions`**

```javascript Your Permissions
const url = 'https://cdn.emnify.net/api/v1/user/my/permission';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Your Permissions`**

```go Your Permissions
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://cdn.emnify.net/api/v1/user/my/permission"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Your Permissions`**

```ruby Your Permissions
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/user/my/permission")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

**`Your Permissions`**

```java Your Permissions
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://cdn.emnify.net/api/v1/user/my/permission")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Your Permissions`**

```php Your Permissions
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://cdn.emnify.net/api/v1/user/my/permission', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

**`Your Permissions`**

```csharp Your Permissions
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/user/my/permission");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Your Permissions`**

```swift Your Permissions
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/user/my/permission")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Response (200)

```json
[
  {
    "id": 2,
    "resource": "Endpoint",
    "action": "List"
  },
  {
    "id": 3,
    "resource": "Endpoint",
    "action": "Create"
  },
  {
    "id": 4,
    "resource": "Endpoint",
    "action": "Delete"
  },
  {
    "id": 5,
    "resource": "ServicePolicy",
    "action": "List"
  }
]
```

## Troubleshooting

| Response                   | Cause                                                                                                                                   | Fix                                                                                                                                 |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`          | The request includes both `permissions` and `permission_ids`.                                                                           | Send only one of the two fields.                                                                                                    |
| `400 Bad Request`          | A permission isn't available to your Workspace type, for example `Permission(s) not allowed for this organisation type`.                | Remove that pair from the request. Use the [permission catalog](#list-available-permissions) to see what your Workspace can assign. |
| `403 Forbidden`            | Your role doesn't include the required permission.                                                                                      | See [Permission errors](/developers/api-guidelines/errors#permission-errors).                                                       |
| `409 Conflict`             | A role with this name already exists, the user already holds the role, or you removed a user's last role.                               | Rename the role, or adjust the assignment.                                                                                          |
| `422 Unprocessable Entity` | You modified a system role, deleted a role that's still assigned, reached the custom role limit, or referenced a deprecated permission. | Target a custom role, unassign users first, or remove the deprecated permission.                                                    |

For how permissions work and which permissions each system role includes, see [Permissions](/developers/api-guidelines/permissions).
For ready-made permission sets covering three common jobs, see [Role templates](/developers/api-guidelines/role-templates).

Role changes raise system events, so you can audit who created, edited, or deleted a custom role, and when a user's role changed.
See [User management events](/system-events/event-types/user-management).