> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usehasp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scope Grants

> RFC 9396-shaped authorization objects that define what an agent credential is permitted to do.

Scope grants define what an agent is authorized to do under a credential. They follow the [OAuth 2.1 Rich Authorization Requests (RFC 9396)](https://www.rfc-editor.org/rfc/rfc9396) shape: structured authorization objects with a `type` field and optional type-specific constraints.

A credential may carry multiple scope grants. A tool invocation is authorized if it satisfies *any* scope grant. Each grant is satisfied only if *all* its constraints are met.

The supported grant types are published in the [discovery document](/platform/oauth/discovery) as `authorization_details_types_supported`:

| Type                   | Description                                                                                              |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| `hasp.data.read`       | Read records from an app, optionally narrowed to specific entities, fields, and filters.                 |
| `hasp.data.write`      | Write records to an app, optionally narrowed to specific entities and fields.                            |
| `external.tool.invoke` | Invoke one named external tool. One grant per tool.                                                      |
| `tool.destructive`     | Invoke one named destructive capability. Wildcards are rejected — the tool must be named.                |
| `agent.delegate`       | Delegate authority to another agent, bounded by `max_chain_depth` (1–10).                                |
| `agent.compose`        | Compose with another agent by `agent_id`.                                                                |
| `human.escalate`       | Escalate to a human — exactly one of `to_role` or `to_user_id`.                                          |
| `workflow.schedule`    | Create scheduled runs for named workflows, bounded by horizon, interval, live-schedule count, and depth. |
| `workflow.invoke`      | Invoke named workflows over A2A. The wildcard `*` is permitted only as the sole element.                 |

## Scope type reference

### `hasp.data.read`

Authorize the agent to read records from the HASP Data API.

```json theme={null}
{
  "type": "hasp.data.read",
  "app_id": "app_01ARZ3...",
  "entities": ["patient_intake", "patient_profile"],
  "filters": {
    "patient.assigned_clinician_id": "{{delegating_user.id}}"
  }
}
```

| Field      | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| `app_id`   | Restrict to a specific app's data. Omit to allow reads across all apps.                          |
| `entities` | Restrict to specific entity types. Omit to allow all readable entities.                          |
| `filters`  | Field-level filters applied to every read. Supports [substitution syntax](#substitution-syntax). |

***

### `hasp.data.write`

Authorize the agent to create or update records in the HASP Data API.

```json theme={null}
{
  "type": "hasp.data.write",
  "app_id": "app_01ARZ3...",
  "entities": ["scheduling_request"],
  "fields": ["requested_specialty", "requested_window", "notes"]
}
```

| Field      | Description                                                                           |
| ---------- | ------------------------------------------------------------------------------------- |
| `app_id`   | Restrict to a specific app.                                                           |
| `entities` | Restrict to specific entity types.                                                    |
| `fields`   | Restrict to specific writable fields. The agent cannot write fields not in this list. |

`hasp.data.read` does not implicitly grant `hasp.data.write`. A separate grant is required for write access even on the same entities.

***

### `external.tool.invoke`

Authorize the agent to call an external or registered tool.

```json theme={null}
{
  "type": "external.tool.invoke",
  "tool_id": "calendar.find_slots",
  "rate_limit": 60,
  "constraints": {
    "from_address": ["scheduling@example.com"],
    "templates_only": true
  }
}
```

| Field         | Description                                                                                     |
| ------------- | ----------------------------------------------------------------------------------------------- |
| `tool_id`     | The registered tool identifier. Required.                                                       |
| `rate_limit`  | Optional rate limit: maximum invocations per hour (integer). Omit for no rate limit.            |
| `constraints` | Arbitrary key-value constraints enforced at invocation time. Keys and values are tool-specific. |

Each `external.tool.invoke` grant applies to a single tool. To authorize multiple tools, add one grant per tool.

***

### `agent.delegate`

Authorize the agent to issue a child credential to another registered agent.

```json theme={null}
{
  "type": "agent.delegate",
  "to_agent_id": "agent_followup_01J...",
  "max_chain_depth": 1
}
```

| Field             | Description                                                                                                                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `to_agent_id`     | The agent this credential may delegate to. Required.                                                                                                                                                 |
| `max_chain_depth` | Required. Maximum additional delegation depth from the child, an integer from 1 to 10. `1` means the child cannot further delegate. Exceeding it fails with `AGENT_DELEGATION_CHAIN_DEPTH_EXCEEDED`. |

The child credential's scopes are bounded by the parent's. An agent cannot delegate scopes it does not itself hold.

***

### `human.escalate`

Authorize the agent to escalate to a human via a configured channel.

```json theme={null}
{
  "type": "human.escalate",
  "to_role": "on_call_clinician",
  "channels": ["pager", "in_app"]
}
```

| Field        | Description                                                |
| ------------ | ---------------------------------------------------------- |
| `to_role`    | Org role to escalate to.                                   |
| `to_user_id` | A specific user to escalate to.                            |
| `channels`   | Required, non-empty. Delivery channels for the escalation. |

Supply **exactly one** of `to_role` or `to_user_id`. Sending neither, or both, is rejected at issuance.

***

### `tool.destructive`

Authorize the agent to invoke a single named destructive capability — one whose effects cannot be undone by a subsequent call.

```json theme={null}
{
  "type": "tool.destructive",
  "tool_id": "records.purge"
}
```

| Field     | Description                                    |
| --------- | ---------------------------------------------- |
| `tool_id` | Required. The specific destructive capability. |

A wildcard `tool_id` of `"*"` is **rejected**: a destructive capability must be named explicitly. Grant one per capability.

***

### `agent.compose`

Authorize the agent to compose with another registered agent.

```json theme={null}
{
  "type": "agent.compose",
  "agent_id": "agent_summarizer_01J..."
}
```

| Field      | Description                                           |
| ---------- | ----------------------------------------------------- |
| `agent_id` | Required. The agent this credential may compose with. |

***

### `workflow.invoke`

Authorize the agent to invoke named workflows over [A2A](/studio/a2a-protocol).

```json theme={null}
{
  "type": "workflow.invoke",
  "workflow_ids": ["wf_care_coordination_01J..."]
}
```

| Field          | Description                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `workflow_ids` | Required, non-empty. The workflows this credential may invoke. The wildcard `"*"` is permitted only as the sole element. |

The A2A route requires the `workflow.run` capability, which this grant confers. A credential whose grant names specific workflows is rejected when it invokes any other workflow, even one that is agent-callable in the same org.

***

### `workflow.schedule`

Authorize the agent to create scheduled runs of named workflows.

```json theme={null}
{
  "type": "workflow.schedule",
  "workflow_ids": ["wf_intake_01J..."],
  "max_horizon": "P30D",
  "min_interval": "PT1H",
  "max_live_schedules": 20,
  "max_schedule_depth": 0
}
```

| Field                | Description                                                                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `workflow_ids`       | Required, non-empty. The workflows that may be scheduled. The wildcard `"*"` is permitted only as the sole element, and requires an org-policy opt-in. |
| `max_horizon`        | Required. ISO 8601 duration — how far ahead a run may be scheduled (e.g. `P30D`).                                                                      |
| `min_interval`       | Required. ISO 8601 duration — the shortest permitted gap between runs (e.g. `PT1H`).                                                                   |
| `max_live_schedules` | Required. Non-negative integer cap on concurrently live schedules.                                                                                     |
| `max_schedule_depth` | Required. Non-negative integer — how deep a scheduled run may itself schedule further runs.                                                            |

Tier ceilings apply on top of the grant; the effective bound is the tighter of the two.

***

## Substitution syntax

Filters and constraints may use substitution syntax to bind values at credential issuance time:

| Variable                    | Resolves to                                        |
| --------------------------- | -------------------------------------------------- |
| `{{delegating_user.id}}`    | The ULID of the user who authorized the credential |
| `{{delegating_user.email}}` | The email of the delegating user                   |
| `{{org.id}}`                | The org's ULID                                     |
| `{{org.slug}}`              | The org's slug                                     |
| `{{current_time}}`          | ISO 8601 timestamp at issuance                     |

Substitution happens at **credential issuance**, not at invocation. The credential carries already-resolved values. This makes scope fully auditable — the agent cannot re-evaluate bindings at runtime.

Example:

```json theme={null}
{
  "type": "hasp.data.read",
  "filters": {
    "patient.assigned_clinician_id": "{{delegating_user.id}}"
  }
}
```

Issued as:

```json theme={null}
{
  "type": "hasp.data.read",
  "filters": {
    "patient.assigned_clinician_id": "01JQUSER0000000000000000"
  }
}
```

***

## Allowed scope types

Each agent definition has an `allowed_scope_types` list. A credential cannot be issued with a scope type that is not in the agent's allowed types.

To configure which scope types an agent may receive:

* **Dashboard:** Developers → Agents → \[agent name] → Settings
* **API:** `PATCH /v1/agents/{agent_id}` with `allowed_scope_types`

Setting `allowed_scope_types` to `null` permits all scope types. This is the default for newly registered agents.

***

## Scope type restrictions

Scope types are an **enumerated set** — new types require a platform update. Customers cannot define custom scope types. This is intentional: a scope type the platform does not recognize cannot be enforced or audited, which breaks the regulated-industry trust model.

The current scope type set is:

```
hasp.data.read
hasp.data.write
external.tool.invoke
agent.delegate
human.escalate
```
