---
title: Policies and permissions
description: The policy document language, how Hexel Studio decides a request,
  how access flows from an organization to its workspaces and environments, and
  the managed policies you can use.
url: https://hexelstudio.com/docs/identity/policies
---

A policy is a JSON document that lists actions. You attach it to a member, a group, a service user or a role, and that principal may then perform the actions it allows. Hexel Studio denies everything a policy does not allow.

This page describes the language exactly as Hexel Studio reads it today, including the parts it stores but does not apply yet. To create and attach policies, see [Write and attach policies](https://hexelstudio.com/docs/identity/write-policies).

## A policy document

```json
{
  "Version": "2026-02-17",
  "Statement": [
    {
      "Sid": "ReadWorkspaces",
      "Effect": "Allow",
      "Action": ["workspace:workspace:read", "env:env:read"],
      "Resource": ["*"]
    }
  ]
}
```

| Field | Type | What Hexel Studio does with it |
| --- | --- | --- |
| `Version` | string | Stored. Any value is accepted and none changes the meaning. Use `2026-02-17`, the value of the managed policies. |
| `Statement` | array | The statements. A document needs at least one. |
| `Sid` | string, optional | A label for the statement. Stored, not used in decisions. |
| `Effect` | `"Allow"` or `"Deny"` | `Allow` grants the listed actions. `Deny` is stored but does not block normal API calls yet ([see below](#what-is-not-enforced-yet)). The value is case-sensitive: `"allow"` grants nothing. |
| `Action` | string or array of strings | The actions the statement covers. See [Action names and wildcards](#action-names-and-wildcards). |
| `Resource` | string or array of strings | Required by the [policy validator](https://hexelstudio.com/docs/identity/write-policies#validate-a-policy). Stored, not matched. Use `["*"]`. |
| `Condition` | object, optional | Stored, never evaluated. |

<Warning>
`Resource` and `Condition` do not narrow a statement. An `Allow` statement grants its actions on every resource in the organization, whatever `Resource` or `Condition` says. Don't write a policy that relies on them to limit access. To grant access to one workspace or environment only, use a [permission set](#permission-sets) or a [workspace-scoped role](https://hexelstudio.com/docs/identity/roles). See [Fine-grained access](https://hexelstudio.com/docs/identity/fine-grained-access) for what is planned.
</Warning>

A document that is not valid JSON, or has no statement Hexel Studio can read, grants nothing. Hexel Studio does not check the document when you create a policy, and when you change one it checks only that the document is JSON. [Validate it](https://hexelstudio.com/docs/identity/write-policies#validate-a-policy) first.

## Action names and wildcards

An action is named `<service>:<resource>:<verb>`, for example `workspace:workspace:create` or `iam:policy:write`. Names are case-sensitive. The [actions reference](https://hexelstudio.com/docs/identity/actions) lists every action.

A pattern in `Action` matches an action when it is:

| Pattern | Matches | Example |
| --- | --- | --- |
| `*` | Every action | `"*"` |
| An exact name | That action only | `"iam:policy:read"` |
| A name ending in `*` | Every action that starts with the text before the `*` | `"iam:*"` (all IAM actions), `"iam:policy:*"` (all policy actions) |

Only a trailing `*` is a wildcard. `iam:*:read` matches nothing except an action literally named `iam:*:read`. A trailing `*` matches any text, so `iam*` also matches actions of any service whose name starts with `iam`.

## How a request is decided

Your normal API calls are decided by the **cached permission check**. For each request, Hexel Studio collects the `Allow` actions of every policy that applies to the caller in the token's organization, and allows the request if one of them matches the action the API checks. If none matches, the request is denied with `403`. `Deny` statements are not read by this check, so they have no effect on API calls today.

The policies that apply depend on who is calling:

| Caller | Policies that apply |
| --- | --- |
| Member | Policies attached to the member, policies attached to every group the member is in (including the [membership group](https://hexelstudio.com/docs/identity/principals#what-a-member-gets-by-default) of their member role), and the policies of [permission sets](#permission-sets) assigned to the member or their groups |
| Service user | Policies attached to the service user, to its groups, and of permission sets assigned to it |
| Role session | Only the policies attached to the role, limited by the role's permission boundary if it has one. The policies of the person or service user who assumed the role don't apply. See [Roles](https://hexelstudio.com/docs/identity/roles). |

A permission never comes from a name, an email address or a header you send. Permissions are not stored in the access token, so a change applies to tokens that were already issued.

When you change an attachment, a group, a permission set or a policy document, Hexel Studio updates the cached permissions of the principals concerned, usually at once. Cached permissions also expire on their own, so a change always takes effect within about a minute.

<Note>
The [test access](https://hexelstudio.com/docs/identity/test-access) endpoints use a fuller evaluator that also applies `Deny` statements and Preview guardrails. Their answer can differ from what your API calls get. See [Test access](https://hexelstudio.com/docs/identity/test-access#how-these-results-differ-from-real-calls).
</Note>

## Organization, workspace and environment scope

A request is made for one organization: the token's. It can also name a workspace and an environment of that organization, with the `X-Workspace-Id` and `X-Environment-Id` headers (see [Headers](https://hexelstudio.com/docs/identity/headers)). The endpoints for a single workspace or environment, such as `PATCH /v1/workspaces/<workspace-id>`, set this scope from the path for you.

Each grant has a scope, and a grant at one level applies to everything below it:

| Where the grant is placed | Applies to requests for |
| --- | --- |
| Organization: policy attachments, permission sets assigned without a workspace, organization-scoped roles | The organization and every workspace and environment in it |
| Workspace: permission sets assigned with `workspace_id`, workspace-scoped roles | That workspace and its environments |
| Environment: permission sets assigned with `workspace_id` and `environment_id`, environment-scoped roles | That environment only |

Policy attachments have no scope of their own: a policy attached to a member, group or service user always applies organization-wide.

Hexel Studio checks the scope before it looks at any permission:

- A request that names an environment must also name its workspace.
- The workspace must be an active workspace of the token's organization, and the environment must be an active environment of that workspace. Otherwise the request is denied, even for an owner.
- A request that names no workspace is checked against organization-level grants only. A grant placed on a workspace does not count for it.

Some actions are always checked at organization level, whatever headers you send. Creating a workspace (`workspace:workspace:create`) is one: a grant placed on a workspace can't let someone create workspaces.

## Managed policies

Hexel Studio provides managed policies that every organization can attach. They appear in `GET /v1/policies` next to your own policies, with `"is_system": true`. You can attach them and read them, but you can't change or delete them.

These managed policies are the ones the [default member roles](https://hexelstudio.com/docs/identity/principals#what-a-member-gets-by-default) use, plus a few full-access ones:

| Policy ID | Name | Allows |
| --- | --- | --- |
| `pol_org_owner_full` | Organization Owner Full Access | `*` |
| `pol_iam_full` | IAM Full Access | `iam:*` |
| `pol_iam_admin` | IAM Admin Access | Read and write IAM: policies, attachments, groups and their members, roles and trust policies, permission sets, `iam:authz:simulate`, and the Preview IAM features. Not `iam:user:erase`. |
| `pol_iam_readonly` | IAM Read-Only Access | The `:read` IAM actions, and `iam:tag:manage` |
| `pol_org_full` | Organization Full Access | `org:*` |
| `pol_org_member` | Organization Member Access | `org:org:read`, `org:org:update`, `org:member:invite`, `org:member:manage`, `org:settings:read`, `org:settings:write`, `org:billing:read`, `org:audit:read` |
| `pol_org_readonly` | Organization Read-Only | `org:org:read`, `org:settings:read`, `org:billing:read`, `org:audit:read` |
| `pol_billing_full` | Billing Full Access | `org:billing:read`, `org:billing:manage` |
| `pol_billing_readonly` | Billing Read-Only Access | `org:billing:read` |
| `pol_ws_full` | Workspace Full Access | `workspace:*` |
| `pol_ws_readonly` | Workspace Read-Only Access | `workspace:workspace:read` |
| `pol_env_full` | Environment Full Access | `env:*` |
| `pol_env_readonly` | Environment Read-Only Access | `env:env:read` |
| `pol_svc_account` | Service Account Full Access | `directory:service_account:create`, `:read`, `:update`, `:delete`, `:manage_keys` |
| `pol_dir_readonly` | Directory Read-Only Access | Includes `directory:service_account:read` |
| `pol_data_full` / `pol_data_readonly` | Atlas Full Access / Atlas Read-Only Access | Atlas actions |
| `pol_memory_full` / `pol_memory_user` / `pol_memory_readonly` | Memory Full / User / Read-Only Access | Hexel Managed Memory actions |
| `pol_skills_full` / `pol_skills_publisher` / `pol_skills_readonly` | Skills Full / Publisher / Read-Only Access | Skills Registry actions |

Read a managed policy's document with `GET /v1/policies/<policy-id>`. The list also contains policies for other Hexel Studio products and policies Hexel Studio uses for its own services. The [actions reference](https://hexelstudio.com/docs/identity/actions) describes the actions.

## Permission sets

A permission set is a named list of policy IDs. Assigning it to a member, a group or a service user grants all its policies to that principal, optionally only in one workspace or environment, and optionally until an expiry time. It is the way to grant a member access to a single workspace or environment.

- An assignment grants its policies while it exists and has not expired. Deleting the assignment removes the access.
- An assignment with `workspace_id` grants in that workspace and its environments. With `workspace_id` and `environment_id`, it grants in that environment only. Without either, it grants organization-wide.
- The policy list is copied when you assign the set. Changing the set's `policy_ids` later does not change existing assignments; changing a policy's document does.

`GET /v1/permission-sets` lists your permission sets and any that Hexel Studio provides. See [Grant access in one workspace or environment](https://hexelstudio.com/docs/identity/write-policies#grant-access-in-one-workspace-or-environment) for the API.

## Role templates

`GET /v1/role-templates` and `GET /v1/role-templates/<name>` return role templates: a `name`, `display_name`, `description`, `scope` and a list of action names in `permissions`. They are reference lists you can copy actions from into a policy. A template grants nothing, and Hexel Studio does not create policies or roles from it. Reading templates needs `iam:policy:write`.

## Nobody grants what they don't hold

To attach a policy, assign a permission set, add a member to a group, or change the document of a policy that is in use, you must already hold every action the change grants, at organization level. Otherwise the request is refused with `403`:

```json
{
  "error": "forbidden",
  "reason": "cannot grant actions the caller does not hold",
  "missing_actions": ["iam:user:erase"]
}
```

An owner holds `*`, so an owner can grant anything. An admin can grant only what the admin policies give them. A wildcard counts only if you hold an equal or wider one: holding `iam:policy:read` and `iam:policy:write` does not let you grant `iam:policy:*`.

## What is not enforced yet

These parts of the language are accepted and stored, but don't change the result of your API calls yet:

| Feature | Today | Read more |
| --- | --- | --- |
| `Deny` statements | Not applied to API calls. Only the [test access](https://hexelstudio.com/docs/identity/test-access) endpoints apply them. Remove the `Allow` instead. | [Guardrails](https://hexelstudio.com/docs/identity/guardrails) (Preview) |
| `Resource` and `Condition` | Stored and ignored everywhere | [Fine-grained access](https://hexelstudio.com/docs/identity/fine-grained-access) (Preview) |
| Permission boundaries on members and service users, service control policies, resource control policies | Not applied to API calls | [Guardrails](https://hexelstudio.com/docs/identity/guardrails) (Preview) |

A permission boundary set on a role does apply: it caps every call made with that role's sessions. See [Roles](https://hexelstudio.com/docs/identity/roles).
