> 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-user-subject-key-binding.md).

# External User Subject-Key Binding

External user subject-key binding lets your backend bind the customer user who opened an Amigo session to a parameter used by an agent-callable Platform Integration. The bound value comes from the external-user session token, not from the model, the browser, or normal tool input.

Use this pattern when your upstream API mints user-scoped tokens. For example, your token exchange may require a customer user ID, clinician email, provider ID, or account ID before it returns a bearer token that can access that user's records.

{% hint style="warning" %}
Do not expose the bound identity parameter in the tool schema or accept it from the browser. Your backend should authenticate the customer user, mint the external-user session with `external_subject_key`, and let Amigo inject that verified value into integration auth through `identity_bindings`.
{% endhint %}

## When To Use This

Use subject-key binding when all of these are true:

* Your customer-hosted app authenticates the end user before starting an Amigo session.
* Your backend mints external-user session tokens through an External Integration credential.
* An agent-callable REST integration uses `custom_token_exchange`.
* The token exchange needs a per-user parameter such as `user_id`, `user_email`, or `provider_id`.
* The model should never choose or override that parameter.

For browser-hosted chat setup, start with [Serve an Agent From Your Web Application](/developer-guide/platform-api/conversations/serve-agent-from-web-app.md). This guide adds the integration-auth binding layer on top of that session flow.

## Concepts

There are two different integration records involved:

| Concept              | Purpose                                                         | Forge command family                      |
| -------------------- | --------------------------------------------------------------- | ----------------------------------------- |
| External Integration | Credentials for your backend to mint external-user sessions.    | `forge platform external-integration ...` |
| Platform Integration | Agent-callable REST API connection, auth config, and endpoints. | `forge platform integration ...`          |

The External Integration proves that your backend may create an external-user session for an allowed `service_id`. The Platform Integration is the API the agent later calls during the session.

The binding connects them at runtime:

1. Your backend authenticates the customer user.
2. Your backend mints an external-user session with `external_subject_key`.
3. The browser uses the external-user access token for conversation calls.
4. When the agent calls a Platform Integration endpoint, Amigo resolves `external_user.subject_key` from the verified session.
5. Amigo passes that value as an auth-only parameter to `custom_token_exchange`.
6. The auth-only value is stripped before the downstream API request is built.

The result is a one-way binding from the authenticated customer session to the customer API token exchange.

{% hint style="info" %}
This integration-auth binding does not itself attach the caller to a world entity or load returning-user history and memory.
{% endhint %}

## Choose The Subject Key

`external_subject_key` is customer-owned identity material. Choose the value your backend can verify before minting the session and your token exchange can authorize.

Recommended choices:

* An immutable internal user ID when your token exchange accepts one.
* A canonical provider or employee ID when that is the upstream authorization key.
* A verified email address only when the upstream token exchange requires email and your backend owns email-change handling.

The key must be non-empty, should be stable for the user's account, and must not come from model output, browser-local state, query strings, or normal conversation input.

## Configure Forge

Install Agent Forge Go and configure a Platform profile or environment variables. The examples below use `--env staging`; use your profile name or omit `--env` if you rely entirely on process environment variables.

```bash
forge --version

export PLATFORM_API_URL="https://api.platform.amigo.ai"
export PLATFORM_WORKSPACE_ID="<workspace-id>"
export PLATFORM_API_KEY="<admin-or-owner-platform-api-key>"
```

The API key used for setup must be able to manage external integrations, Platform Integrations, skills, and any service configuration you update.

## Create The External Integration Credential

Create an External Integration for the backend that will mint browser session tokens:

```bash
forge platform external-integration create \
  --body '{
    "name": "customer_portal",
    "display_name": "Customer Portal",
    "description": "Backend-for-frontend that mints external-user sessions"
  }' \
  --env staging \
  --json
```

Save the returned `id`:

```bash
export AMIGO_EXTERNAL_INTEGRATION_ID="<external-integration-id>"
```

Create a credential scoped to the service your app exposes:

```bash
forge platform external-integration credential create "$AMIGO_EXTERNAL_INTEGRATION_ID" \
  --body '{
    "name": "customer-portal-prod",
    "service_ids": ["<service-id>"]
  }' \
  --env staging \
  --json
```

Store the returned `client_id` and one-time `client_secret` in your backend secret manager:

```bash
export AMIGO_EXTERNAL_INTEGRATION_CLIENT_ID="<client-id>"
export AMIGO_EXTERNAL_INTEGRATION_CLIENT_SECRET="<client-secret>"
export AMIGO_SERVICE_ID="<service-id>"
```

If the secret is lost or exposed, rotate it:

```bash
forge platform external-integration credential rotate \
  "$AMIGO_EXTERNAL_INTEGRATION_ID" "<credential-id>" \
  --env staging \
  --json
```

## Mint The External-User Session

Your backend first exchanges the External Integration credential for a parent token:

```bash
curl -s "$PLATFORM_API_URL/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$AMIGO_EXTERNAL_INTEGRATION_CLIENT_ID" \
  -d "client_secret=$AMIGO_EXTERNAL_INTEGRATION_CLIENT_SECRET" \
  -d "scope=external_user_sessions:create"
```

Then it creates an external-user session. Set `external_subject_key` only after your backend authenticates the user in your application:

```bash
curl -s "$PLATFORM_API_URL/token" \
  -H "Authorization: Bearer $AMIGO_PARENT_ACCESS_TOKEN" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=external_user_session" \
  -d "workspace_id=$PLATFORM_WORKSPACE_ID" \
  -d "service_id=$AMIGO_SERVICE_ID" \
  -d "subject_type=user" \
  -d "external_subject_key=customer-user-123" \
  -d "ttl_seconds=1800" \
  -d "scope=conversations:create conversations:turn conversations:read_own conversations:close_own"
```

Return only the external-user access token and non-secret metadata to the browser. Store refresh tokens server-side as described in [Serve an Agent From Your Web Application](/developer-guide/platform-api/conversations/serve-agent-from-web-app.md).

## Bind The Subject Key To Integration Auth

Configure the agent-callable Platform Integration with `custom_token_exchange` and `auth.identity_bindings`.

This example binds the verified external subject key to the `user_id` token-exchange parameter and sends it to your token exchange in the `X-User-ID` header:

```json
{
  "name": "customer_api",
  "display_name": "Customer API",
  "base_url": "https://api.customer.example",
  "enabled": true,
  "approval_policy": "none",
  "secret_value": "<workspace-token-exchange-secret>",
  "auth": {
    "type": "custom_token_exchange",
    "exchange_url": "https://api.customer.example/auth/agent-token",
    "body_encoding": "json",
    "static_headers": {
      "X-Auth-Secret": "{secret}"
    },
    "static_body_fields": {},
    "param_headers": {
      "X-User-ID": {
        "param_name": "user_id",
        "description": "Authenticated customer user ID"
      }
    },
    "param_body_fields": {},
    "identity_bindings": {
      "user_id": "external_user.subject_key"
    },
    "response_token_path": "/access_token"
  }
}
```

Create the Platform Integration with Forge:

```bash
forge platform integration create \
  --file customer-api-integration.json \
  --env staging
```

To update an existing integration, use a patch body. Omit `secret_value` unless you intentionally want to rotate the stored workspace secret:

```json
{
  "auth": {
    "type": "custom_token_exchange",
    "exchange_url": "https://api.customer.example/auth/agent-token",
    "body_encoding": "json",
    "static_headers": {
      "X-Auth-Secret": "{secret}"
    },
    "static_body_fields": {},
    "param_headers": {
      "X-User-ID": {
        "param_name": "user_id",
        "description": "Authenticated customer user ID"
      }
    },
    "param_body_fields": {},
    "identity_bindings": {
      "user_id": "external_user.subject_key"
    },
    "response_token_path": "/access_token"
  }
}
```

```bash
forge platform integration update "$AMIGO_PLATFORM_INTEGRATION_ID" \
  --file customer-api-integration-auth-patch.json \
  --env staging
```

The binding key, `user_id` in this example, must match a `param_name` declared in `param_headers` or `param_body_fields`. The binding value must be one of:

| Binding source              | Use when                                                                                                                                   |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `external_user.subject_key` | The session's verified `external_subject_key` is enough for the customer token exchange.                                                   |
| `principal.subject_key`     | You use [External Principals](/developer-guide/platform-api/integrations/external-principals.md) and want the role-assignment subject key. |
| `principal.subject_id`      | You use External Principals and the upstream API keys users by a vendor-specific subject ID.                                               |

Use `external_user.subject_key` for the session-auth binding described in this guide. Use `principal.*` only when you have also provisioned external-principal role assignments and need role-scoped authorization.

## Configure The Endpoint And Skill

After the Platform Integration exists, create its endpoint definitions through the Platform API reference, the Console, or Forge (`forge platform integration endpoint-create` and `endpoint-list`). Forge does not expose a nested endpoint update command; update endpoints through the API or Console.

Keep the bound auth parameter out of endpoint input schemas. If `identity_bindings` declares `user_id`, do not add `user_id` as an endpoint input property. Amigo strips bound auth parameters from model-facing schemas and ignores model-supplied values for them.

Wire the endpoint into an orchestrated skill by referencing the integration and endpoint names:

```json
{
  "slug": "lookup-customer-record",
  "name": "Lookup Customer Record",
  "description": "Looks up records from the customer API for the authenticated user.",
  "execution_tier": "orchestrated",
  "system_prompt": "Use the customer API only for the current authenticated user.",
  "input_schema": {
    "type": "object",
    "properties": {
      "record_type": {
        "type": "string",
        "description": "Type of record to retrieve"
      }
    },
    "required": ["record_type"]
  },
  "integration_tools": [
    {
      "integration": "customer_api",
      "endpoint": "get_customer_records"
    }
  ]
}
```

Create or update the skill with Forge:

```bash
forge platform skill create \
  --file lookup-customer-record-skill.json \
  --env staging
```

```bash
forge platform skill update "$AMIGO_SKILL_ID" \
  --file lookup-customer-record-skill.json \
  --env staging
```

If you manage agents, context graphs, and services as local Forge JSON, validate and push them after referencing the new skill:

```bash
forge validate --all --env staging
forge platform push --all --env staging
forge platform push --all --env staging --apply
```

`forge platform push` is dry-run by default. Review the plan before adding `--apply`.

## Test The Binding

Use Forge to inspect the tools available to the service:

```bash
forge platform tool-test resolve \
  --service-id "$AMIGO_SERVICE_ID" \
  --env staging
```

For a bound integration, prefer a principal-bearing session harness or an end-to-end external-user conversation test. The direct integration test endpoint rejects caller-supplied values for identity-bound auth parameters because accepting them would let a tester mint a token for an arbitrary user outside a verified session.

Run these checks before production:

1. Positive path: mint an external-user session with a known `external_subject_key`, call the agent, and verify the customer token exchange receives that subject key.
2. Override attempt: ask the agent to use a different user ID or email. The token exchange should still receive the verified session subject key.
3. Missing context: invoke the bound tool without an external-user session. The tool should fail closed instead of falling back to a model-supplied parameter.
4. Schema check: confirm the bound parameter is absent from the tool schema returned by `forge platform tool-test resolve`.

## Troubleshooting

| Symptom                                                                      | Likely cause                                                                                  | Resolution                                                                                                                        |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `external_subject_key` is missing during session mint                        | The external-user-session request did not include the customer subject key.                   | Send `external_subject_key` from your authenticated backend session.                                                              |
| Integration create/update returns a validation error for `identity_bindings` | The binding key does not match a declared `param_name`, or the binding source is unsupported. | Declare the parameter under `param_headers` or `param_body_fields`, then bind that `param_name` to `external_user.subject_key`.   |
| Customer token exchange receives no user parameter                           | The auth config declares a dynamic token-exchange parameter but does not bind it.             | Add `auth.identity_bindings` for the parameter.                                                                                   |
| Customer token exchange receives the wrong user                              | The backend minted the session with the wrong `external_subject_key`.                         | Fix the backend session bootstrap path. Do not override the value through tool params.                                            |
| Direct integration endpoint testing rejects `auth_params`                    | The parameter is identity-bound and cannot be supplied out of session.                        | Test through an external-user session or a tool/session harness that carries verified binding context.                            |
| A `principal.*` binding fails while `external_user.subject_key` works        | External-principal role assignments have not been materialized for the session.               | Use `external_user.subject_key` for session-only token binding, or complete External Principals setup before using `principal.*`. |

## Related References

* [Serve an Agent From Your Web Application](/developer-guide/platform-api/conversations/serve-agent-from-web-app.md)
* [External Integrations](/developer-guide/platform-api/integrations/external-integrations.md)
* [Integrations](/developer-guide/platform-api/integrations.md)
* [External Principals](/developer-guide/platform-api/integrations/external-principals.md)
* [Tool Testing](/developer-guide/platform-api/functions/tool-testing.md)


---

# 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-user-subject-key-binding.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.
