---
title: Session policies
description: Attach policies to one session to narrow what it can do, and see
  where they are applied today.
url: https://hexelstudio.com/docs/identity/session-policies
---

<Warning>
Session policies do not restrict your normal API calls yet. They are applied only by the full evaluator, which answers `/v1/authz/evaluate`, `/v1/authz/simulate` and `/v1/authz/explain` when you pass a `session_id`. The cached permission check that decides API calls ignores them, and role sessions are never affected. A call that a session policy "denies" still succeeds. This API may change.
</Warning>

## What a session policy is

A session policy is a policy document attached to one session in your organization. It can only narrow: while a session has session policies, an action is allowed in that session only if at least one of its session policies allows the action and none denies it, on top of everything else that is required. A session policy never grants anything.

A **session** is identified by the `sid` claim of an access token:

| Token | Session ID |
| --- | --- |
| A person's access token | Created when they sign in to Hexel Studio, and kept when the access token is refreshed. A new sign-in starts a new session. |
| A service user's access token (API key or OAuth client) | Starts with `svc_`. It is derived from the credential and changes every 15 minutes. |
| A role session | Starts with `rs_`. Session policies never apply to role sessions. |

See [Tokens](https://hexelstudio.com/docs/identity/tokens) for the claims of an access token.

## Where session policies are applied

| Decision | Session policies applied? |
| --- | --- |
| Your normal API calls | No |
| `POST /v1/authz/evaluate`, `/simulate` and `/explain` with `session_id` | Yes |
| Role sessions | No |

When they apply, the evaluator reads every session policy of the session in your organization:

- If the session has none, nothing changes.
- If any session policy has a Deny statement matching the action, the answer is denied with reason `session_policy_denied`.
- If no session policy has an Allow statement matching the action, the answer is denied with reason `session_policy_denied`.
- A session policy whose document cannot be read allows nothing.

Session policies are checked before the policies that grant access, so they also limit ABAC rules. Only the `Action` of each statement is matched: `Resource` and `Condition` are ignored.

## Attach a session policy

`POST /v1/session-policies/{session_id}/policies` needs `iam:relation:write`.

| Field | Type | Description |
| --- | --- | --- |
| `name` | string | Name of the session policy. |
| `document` | string | **Required.** A policy document, encoded as a JSON string. It needs at least one statement, and every statement needs `Effect` (`Allow` or `Deny`), a non-empty `Action` and a `Resource`. |

```bash
curl -X POST https://api.hexelstudio.com/v1/session-policies/<session-id>/policies \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "read-only",
    "document": "{\"Version\": \"2026-02-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"workspace:workspace:read\"], \"Resource\": \"*\"}]}"
  }'
```

The response is `201 Created`:

```json
{
  "id": "sp_...",
  "session_id": "<session-id>",
  "organization_id": "<org-id>",
  "name": "read-only",
  "document": "{\"Version\": \"2026-02-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"workspace:workspace:read\"], \"Resource\": \"*\"}]}",
  "created_at": "2026-10-20T09:00:00Z"
}
```

An invalid document answers `400` with the problems found:

```json
{"error": "session policy document is not valid: statement[0]: missing 'Resource'"}
```

Hexel Studio does not check that the session exists or belongs to someone in your organization. The session policy is stored in your organization and applies only to decisions made in your organization.

## List and delete

| Call | Permission | Response |
| --- | --- | --- |
| `GET /v1/session-policies/{session_id}/policies` | `iam:relation:read` | Array of the session's session policies in your organization |
| `DELETE /v1/session-policies/{session_id}/policies/{policy_id}` | `iam:relation:write` | `204`, or `404` |

## See the effect in the full evaluator

Pass the session ID to `POST /v1/authz/evaluate` to see what the full evaluator decides in that session. It needs `iam:authz:simulate`. See [Test access](https://hexelstudio.com/docs/identity/test-access) for the rest of this API.

```bash
curl -X POST https://api.hexelstudio.com/v1/authz/evaluate \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "subject_id": "<user-id>",
    "organization_id": "'"$ORG_ID"'",
    "session_id": "<session-id>",
    "action": "workspace:workspace:delete"
  }'
```

```json
{
  "allowed": false,
  "reason": "session_policy_denied",
  "cacheable": true,
  "principal": "<user-id>",
  "action": "workspace:workspace:delete",
  "resource_type": "",
  "workspace_id": "",
  "environment_id": "",
  "evaluation_ms": 4
}
```

The same member calling `DELETE /v1/workspaces/...` for real is not stopped by the session policy.
