> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/platform-api/integrations/external-principals.md).

# External Principals

External principals let you bind an external user identity to a workspace entity and scope which tools, skills, and data that user's agent session can access. This is designed for scenarios where an end user - such as a clinician, care coordinator, or partner employee - drives a conversation through the agent and should only see or invoke resources their role permits.

## How It Works

External principals connect three concepts:

1. **Role registry** - Each workspace defines a set of internal roles (normalized lowercase slugs) that map to external role names from the customer's identity system. Roles are workspace-scoped and deduplicated at the registry level.
2. **Role assignments** - An assignment record binds an external user (identified by a subject key such as an email address, and optionally a vendor-specific subject ID) to a workspace entity and one or more internal roles. Assignments carry a status (active, suspended, or revoked) and track how they were provisioned (roster sync or just-in-time). Role verification status is recorded separately - a session opened with unverified roles can behave differently from one with verified roles.
3. **Role grants** - Grants map a registry role to a specific resource (integration endpoint, skill, or knowledge base scope) with a read or write access level. Grants are immutable - changes insert a new active row and mark the prior one as superseded, so the authorization state at any past point in time is reconstructable for audit.

## Management Endpoints

Each of the three concepts has its own workspace-scoped resource family.

### Roles

Register the workspace's internal roles and their external role name mappings.

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-roles" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-roles" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Get a Role

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-roles/{role\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Delete a Role

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-roles/{role\_id}" method="delete" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Role Assignments

Bind an external user (subject key, optional subject ID) to a workspace entity and one or more roles. The `PUT` upserts the assignment for a subject.

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-role-assignments" method="put" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-role-assignments" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Get an Assignment

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-role-assignments/{assignment\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Delete an Assignment

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/external-role-assignments/{assignment\_id}" method="delete" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Role Grants

Map a role to a resource with a read or write access level.

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/role-grants" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/role-grants" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Get a Grant

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/role-grants/{grant\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

#### Delete a Grant

Deleting a grant supersedes it rather than erasing the immutable audit history.

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/role-grants/{grant\_id}" method="delete" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

## Session Resolution

When a session opens anchored to an entity that has an active role assignment, the platform resolves a session principal. The principal carries:

* The external user's identity (subject key and optional subject ID)
* The union of roles from the assignment
* The allow-grants derived from those roles
* The workspace's set of restricted resources (any resource referenced by any active grant)

This resolution happens once at session open. The principal is then used for two purposes throughout the session:

### Grant Gating

A resource is **restricted** if any active grant in the workspace references it. Restriction is a workspace-level fact, not a per-principal one. When a tool call targets a restricted resource, the platform checks whether the principal's roles grant the required access level:

* A **write** grant implies **read** (you can read what you can write)
* A **read** grant does not imply **write**
* Access is allow-union across all of the principal's roles - there is no deny

Unrestricted resources (those with no grant rows in the workspace) remain workspace-open. Workspaces that define no grants are completely unaffected.

### Identity Injection

For integrations that use token exchange authentication, you can declare **identity bindings** that map authentication parameters to the principal's verified identity attributes (subject key or subject ID). At dispatch time, the bound values are resolved from the principal and injected into the token exchange request. The model never supplies these values - they are stripped from the tool schema presented to the agent and removed from any model-supplied parameters before execution.

This closes the per-user identity impersonation hole: the agent cannot supply or override a bound identity parameter, and a missing principal value fails the call closed rather than proceeding without identity.

### Param Bindings

Grants can carry an optional parameter binding constraint (for example, scoping a provider ID parameter to the principal's subject ID). When a grant includes a param binding, the bound value is resolved from the principal and injected into the call parameters at dispatch time. A binding that cannot resolve from the principal fails the call closed.

## Integration with Approval Gates

On text channels, the approval gate runs at dispatch, so an approval-gated write can be parked for operator review before grant gating is evaluated. The principal's grants are enforced when the write actually executes: if an operator approves a call the principal's grants deny, the write is still refused at execution. A grant-denied call may therefore appear in the approval queue, but it can never execute.

## Sessions Without a Principal

Sessions anchored to entities without an active role assignment proceed without a principal. All tools and resources remain workspace-open, and the session behaves exactly as it did before external principals were introduced. This is the expected path for patient-facing sessions and any workspace that has not configured external roles.

## Identity Bindings on Custom Token Exchange

When configuring a custom token exchange integration, the `identity_bindings` field maps declared parameter names to principal attributes:

* `principal.subject_key` - the external user's primary identity (typically an email address)
* `principal.subject_id` - the external user's vendor-specific identifier (when available)

For deployments that only need to bind the authenticated external-user session subject into token exchange auth, use `external_user.subject_key` as described in [External User Subject-Key Binding](/developer-guide/platform-api/integrations/external-user-subject-key-binding.md). Use `principal.*` when the binding must come from materialized External Principals role assignments.

Each bound parameter name must reference a parameter declared on the integration's token exchange headers or body fields. Bindings for undeclared parameters are rejected at configuration time.

A bound parameter is:

* Removed from the tool schema the agent sees
* Stripped from any model-supplied parameters at dispatch
* Injected from the verified principal before the token exchange runs

If the principal does not have a value for a bound attribute (for example, `principal.subject_id` when no subject ID was recorded), the call fails closed rather than proceeding with an absent identity.

## Example: Per-Clinician Access

External principals are the mechanism for embedding an agent that serves many end users under one workspace, where each user must only reach what their role allows - for example a clinician-support agent embedded in a partner's application, where each clinician holds one or more roles and should only invoke the tools and read the data their roles permit.

The flow:

1. The external system authenticates the clinician and resolves the clinician's pre-provisioned role assignment to its workspace entity.
2. A trusted backend creates the conversation with that `entity_id` using a workspace credential that has `Conversation:CreateForPrincipal`. The API derives all principal roles and grants from the active assignment; the client cannot submit them directly.
3. The session loads the principal for that entity. As the agent calls tools, each call is gated by the principal's grants, and identity-bound parameters are injected from the principal rather than model input.

```mermaid
sequenceDiagram
    participant Ext as External system
    participant App as Trusted backend
    participant Sess as Agent session
    participant Disp as Tool dispatch
    participant Vendor as Integration
    Ext->>App: authenticate clinician
    App->>App: resolve assigned workspace entity
    App->>Sess: create conversation with entity_id
    Sess->>Sess: load principal (union of role grants)
    Sess->>Disp: agent calls a tool
    alt role grants the tool
        Disp->>Disp: inject bound identity (e.g. provider ID)
        Disp->>Vendor: call scoped to the user's own identity
        Vendor-->>Sess: result
    else not granted
        Disp-->>Sess: denied (fail closed, never dispatched)
    end
```

Because the scoping is per end user, this is distinct from the workspace-scoped [MCP Server](/developer-guide/platform-api/functions/mcp-server.md), which serves an entire workspace to a single credential and is not per-user scoped.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.amigo.ai/developer-guide/platform-api/integrations/external-principals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
