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

# Workload identity

> Give agents keyless access to your Google Cloud, Microsoft Entra and your own APIs

Every agent has its own **workload identity**: a short-lived, signed token that says which workspace, team and agent is calling. Instead of handing the platform a long-lived service account key, you configure your cloud to trust that identity and grant it exactly the access it needs. No key is stored anywhere.

This uses standard OpenID Connect, so it works with Google Cloud [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation), with [Microsoft Entra Agent ID](https://learn.microsoft.com/en-us/entra/agent-id/) and with any API or gateway that can verify an OpenID Connect token. Today it can be used by the [Google Drive](https://docs.abundly.ai/integrations/google-drive.md) and [BigQuery](https://docs.abundly.ai/integrations/bigquery.md) capabilities, by [MCP servers](https://docs.abundly.ai/integrations/mcp.md) and by [Outgoing APIs](https://docs.abundly.ai/features/http.md#handling-credentials).

> [!NOTE]
>
> Workload identity is an enterprise feature. Contact [support@abundly.ai](mailto:support@abundly.ai) to enable it for your workspace.

## The agent's identity

Workspace admins find the values your cloud needs under **Workspace → Workload identity**:

* **Issuer URL** — the URL your cloud trusts, for example `https://app.abundly.ai/oidc`. It's the same for every agent in your deployment.
* **Workspace ID** — used to restrict the trust to your workspace.

Each agent's **subject** — its unique identity — is `workspace:<workspace id>:team:<team id>:agent:<agent id>` (agents without a team have `team:none`). When a capability is set to workload identity, its settings show the exact principals to grant for that agent, its team and the whole workspace.

Each token also carries these claims. Every claim is always present, empty when it doesn't apply:

| Claim | Contains |
| - | - |
| `workspace_id`, `team_id`, `agent_id` | Stable IDs (`team_id` is `none` without a team). Grant access with these. |
| `workspace_name`, `team_name`, `agent_name` | Display names, for readable bindings and logs. Names can be edited, so never grant access by name. |
| `trigger` | How the run started, for example `chat`, `scheduler`, `email`, `sms`, `webhook`, `http`, `voice` or `agentCommunication` |
| `chat_id` | The chat, for runs in a chat |
| `run_id` | The activity log entry, for triggered runs outside a chat |
| `user_id` | The signed-in user the agent acts for, in chats in the web app. Empty when there's no verified user, for example scheduled runs, inbound email, or Slack. |
| `user_email` | That user's email address, for audit logs and display. Empty whenever `user_id` is. An email can change, and an old address can later belong to someone else, so grant access by `user_id`. |

Tokens are minted per run, so a token issued for one user or run is never reused for another.

For example, the decoded token of an agent in a team, working in a chat started by a signed-in user, used for Google Cloud:

```json theme={null}
{
  "iss": "https://app.abundly.ai/oidc",
  "aud": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/abundly/providers/abundly",
  "sub": "workspace:64f1a2b3c4d5e6f708192a3b:team:69a1c2e4b7d93f0a12c4e8b1:agent:65a0b1c2d3e4f5061728394a",
  "iat": 1791212400,
  "nbf": 1791212400,
  "exp": 1791212700,
  "jti": "3f6c2a9e-8b41-4d7a-9c55-0e2f7b1d4a90",
  "workspace_id": "64f1a2b3c4d5e6f708192a3b",
  "workspace_name": "Acme Logistics",
  "team_id": "69a1c2e4b7d93f0a12c4e8b1",
  "team_name": "Data platform",
  "agent_id": "65a0b1c2d3e4f5061728394a",
  "agent_name": "Data analyst",
  "trigger": "chat",
  "chat_id": "6b02d7e1a4c95f3e8d71b260",
  "run_id": "",
  "user_id": "68f4b0c2d9e17a5b3c6f0d94",
  "user_email": "maria.svensson@acme-logistics.example"
}
```

The same agent on a schedule has `"trigger": "scheduler"`, a `run_id`, and empty `chat_id`, `user_id` and `user_email`.

## Audiences

Every system that trusts your agents gets its own **audience** under **Workspace → Workload identity**. The audience goes into the token, so a token meant for one system can't be used at another. When you add an audience, its type is detected from the value, and the type decides what agents do with the token:

| Type | Detected from | What happens |
| - | - | - |
| **Google Cloud** | A workload identity pool provider's Default audience (`https://iam.googleapis.com/projects/…/providers/…`) | Agents exchange the token at Google's token service for a short-lived Google access token. Used by BigQuery, Google Drive, MCP servers and Outgoing APIs. |
| **Microsoft Entra** | `api://AzureADTokenExchange`, plus your directory (tenant) ID and the agent identity blueprint's application ID | Agents exchange the token at Microsoft Entra for a short-lived token of their own Entra agent identity. Used by MCP servers and Outgoing APIs. |
| **Direct** | Anything else, for example your API's URL | Agents send the token as-is. The receiving system must verify it. Used by MCP servers and Outgoing APIs. |

A token for a Google Cloud or Microsoft Entra audience can be exchanged for access by whoever holds it, so it's only ever sent to that cloud's token service. That's why an audience that looks like a Google one but isn't valid (for example with the project ID instead of the project number) can't be saved, and why an audience's type can't change once it's saved: to change it, delete the audience and add it again.

> [!WARNING]
>
> Always restrict the trust to **your** workspace ID (step 2 below). Without that condition, agents in other workspaces on the same platform could use your pool.

## Set up Google Cloud

**1. Enable the APIs**

In your Google Cloud project, enable the **IAM Service Account Credentials API** and the **Security Token Service API**, plus the APIs the capability uses (Drive, Docs and Sheets for Google Drive; BigQuery for BigQuery).

**2. Create a workload identity pool that trusts Abundly**

Replace the issuer URL and `YOUR_WORKSPACE_ID` with the values from **Workspace → Workload identity**.

```bash theme={null}
gcloud iam workload-identity-pools create abundly \
  --location=global --display-name="Abundly agents"

gcloud iam workload-identity-pools providers create-oidc abundly \
  --location=global --workload-identity-pool=abundly \
  --issuer-uri="https://app.abundly.ai/oidc" \
  --attribute-mapping="google.subject=assertion.sub,attribute.workspace_id=assertion.workspace_id,attribute.team_id=assertion.team_id,attribute.agent_id=assertion.agent_id" \
  --attribute-condition="assertion.workspace_id == 'YOUR_WORKSPACE_ID'"
```

In Google Cloud, a pool trusts an external issuer through what Google calls a *provider*. Open it under **IAM & Admin → Workload Identity Federation** and copy its **Default audience** (`https://iam.googleapis.com/projects/<project number>/locations/global/workloadIdentityPools/abundly/providers/abundly`). That's the value you paste into Abundly.

**3. Grant access**

Choose who gets access with the `--member` value:

| Who | Member |
| - | - |
| One agent | `principal://iam.googleapis.com/projects/<project number>/locations/global/workloadIdentityPools/abundly/subject/<subject>` |
| A team | `principalSet://iam.googleapis.com/projects/<project number>/locations/global/workloadIdentityPools/abundly/attribute.team_id/<team id>` |
| All agents in the workspace | `principalSet://iam.googleapis.com/projects/<project number>/locations/global/workloadIdentityPools/abundly/*` |

**Google Drive** needs a service account, because Drive files are shared with an email address. Create one, let the agent act as it, then share files or folders with the service account's email:

```bash theme={null}
gcloud iam service-accounts add-iam-policy-binding drive-agent@your-project.iam.gserviceaccount.com \
  --role=roles/iam.workloadIdentityUser \
  --member="<member from the table>"
```

**BigQuery** can grant roles to the agent directly, without a service account:

```bash theme={null}
gcloud projects add-iam-policy-binding your-project \
  --role=roles/bigquery.jobUser --member="<member from the table>"
```

Add `roles/bigquery.dataViewer` and `roles/bigquery.metadataViewer` the same way, on the project or on specific datasets. Queries run, and are billed, in a project where the agent has **BigQuery Job User**.

**4. Connect your workspace**

In **Workspace → Workload identity**, add the Default audience under **Audiences**, with a label such as "Google Cloud". Each relying party that trusts your agents gets its own audience in this list, so tokens meant for one can't be used at another.

**5. Configure the capability**

* **BigQuery:** set **Authentication** to **Workload identity** and save. If the workspace has more than one Google Cloud audience, pick which one to use. There's no project to configure: the agent works in the projects you granted it access to. To point agents at the right projects and datasets, add a note to the capability in the workspace or team settings, or keep a data guide as a workspace document.
* **Google Drive:** choose **Workload identity** mode, pick the audience if there are several, enter the service account email, save, then click **Test connection**.

## Set up Microsoft Entra

With [Microsoft Entra Agent ID](https://learn.microsoft.com/en-us/entra/agent-id/), each agent gets an **agent identity** in your directory: an object like a service account that you assign app roles to, for Microsoft Graph or an API of your own such as an MCP server. Agent identities are created under an **agent identity blueprint**, an app registration that represents the Abundly platform and trusts this workspace's issuer.

Entra verifies that the platform is calling; which agent it becomes is decided by the agent identity a workspace admin maps to the agent in Abundly. Google, by contrast, derives the agent from the token itself.

Four things in Entra take part, and the steps below create them in this order:

| In Entra | What it is | What Abundly needs from it |
| - | - | - |
| **Directory (tenant)** | Your organization's Entra directory | Its **Directory (tenant) ID**, a GUID shown on the admin center's Overview page |
| **Agent identity blueprint** | An app registration that represents Abundly in your directory. Agent identities are created under it, and it trusts Abundly's issuer through a federated credential | Its **Application (client) ID**, on the blueprint's Overview page |
| **Agent identity** | One per agent: the object you grant permissions to, like a service account | Its **Application (client) ID**, on the identity's page, or nothing when the platform creates it |
| **Sponsor group** | Optional, for automatic creation: a group that Entra records as accountable for every identity | Its **Object ID**, on the group's page |

**Before you start:** creating the blueprint and its federated credential takes an Entra role that can register applications, such as **Application Administrator**. Granting Microsoft Graph application permissions such as `Sites.Selected` with admin consent takes **Privileged Role Administrator** or **Global Administrator**. Microsoft Entra Agent ID must be available in your directory; the [Agent ID overview](https://learn.microsoft.com/en-us/entra/agent-id/) has its current availability and licensing.

**1. Create the blueprint and trust Abundly**

In the Microsoft Entra admin center, create an [agent identity blueprint](https://learn.microsoft.com/en-us/entra/agent-id/create-blueprint). It is an app registration, so it has the usual pages: note its **Application (client) ID** on the Overview page.

Open the blueprint's **Certificates & secrets → Federated credentials**, add a credential for the scenario **Other issuer** and fill in the values from **Workspace → Workload identity**:

* **Issuer:** the Issuer URL, for example `https://app.abundly.ai/oidc`
* **Subject identifier:** the value shown as **Subject for Microsoft Entra**, `workspace:<your workspace ID>`
* **Audience:** `api://AzureADTokenExchange`

The credential matches the subject exactly, so it is workspace-wide. Give each Abundly workspace its own blueprint: any credential on a blueprint can request tokens for any agent identity under it, so two workspaces sharing one could act as each other's agents.

**2. Connect your workspace**

In **Workspace → Workload identity**, click **Add audience**, choose the type **Microsoft Entra** and enter your directory (tenant) ID and the blueprint's application (client) ID; the audience value `api://AzureADTokenExchange` is filled in for you. Give it a label such as "Microsoft Entra".

![A Microsoft Entra audience under Workspace → Workload identity, with the directory and blueprint IDs filled in and autoprovisioning of agent identities turned on with a sponsor group and a display name prefix](https://mintcdn.com/abundly/dqLER40LB_-nwYZl/images/workload-identity-entra-audience.png?fit=max\&auto=format\&n=dqLER40LB_-nwYZl\&q=85\&s=162f8cff050131240ab84bac7dc5b8ab)

**3. Create an agent identity per agent**

Each agent needs its own agent identity under the blueprint. Either let the platform create them, see [Automatic agent identities](#automatic-agent-identities) below, or [create one](https://learn.microsoft.com/en-us/entra/agent-id/create-delete-agent-identities) per agent yourself and paste its application (client) ID on the agent's **Settings** page under **Entra agent identities**. Until it is set, capabilities using the Entra audience fail with a message saying so.

![The Entra agent identities card on an agent's Settings page, where a workspace admin picks the audience and pastes the agent identity's application ID](https://mintcdn.com/abundly/dqLER40LB_-nwYZl/images/workload-identity-entra-agent-identities.png?fit=max\&auto=format\&n=dqLER40LB_-nwYZl\&q=85\&s=b4fa373c4da49c1578ab4628ffa709ec)

**4. Grant the identity access**

A new agent identity can do nothing. It is not an app registration, so it has no **API permissions** page and no **Grant admin consent** button of its own. Permissions reach it in one of two ways:

* **Once, for every agent:** add the permission to the blueprint, grant admin consent there, and mark it as [inheritable](https://learn.microsoft.com/en-us/entra/agent-id/concept-inheritable-permissions). Every agent identity under the blueprint then holds it. Simplest when all your agents need the same baseline.
* **Per agent:** create an [app role assignment](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-post-approleassignedto) on the identity through Microsoft Graph, or with Microsoft Graph PowerShell (`New-MgServicePrincipalAppRoleAssignment`). It names three things: the agent identity as the principal, the resource's service principal (Microsoft Graph, or your API) and the role's ID. There is no admin center page for this.

What to grant, by target:

* **Microsoft Graph:** the Graph application permissions the agent needs, for example `User.Read.All` to look up people.
* **Your own API or MCP server:** on the API's app registration, set an **Application ID URI** (`api://…`, this is the scope you enter in Abundly) and [define an app role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-add-app-roles-in-apps) whose allowed member type is *Applications*. Then assign that role to the agent identity as above.

**5. Configure the capability**

* **MCP server:** when connecting, choose the **Workload identity** tab, pick the Entra audience and enter the scope, the MCP server's app ID URI with `/.default`, for example `api://mcp-gateway/.default`. Choosing an Entra audience here takes a workspace admin, since it decides where the agent's identity token is sent.

  Your server validates the token like any Entra application token: the issuer is `https://login.microsoftonline.com/<directory ID>/v2.0`, the audience is your API's app ID URI or application ID, as Entra issues it, and the `roles` claim lists the app roles assigned to the identity. The caller's application ID is in `azp`: that is the agent identity, so you can tell agents apart.

> [!WARNING]
>
> As of October 2026, Microsoft's SharePoint and Exchange services reject tokens of agent identities, even with the right permissions; Microsoft has acknowledged the gap without a date. That is why the [SharePoint](https://docs.abundly.ai/integrations/sharepoint.md) capability doesn't offer workload identity yet: keep it on App-only mode. Directory calls to Microsoft Graph and your own APIs or MCP servers work.

### Automatic agent identities

Turn on **Autoprovision agent identities in Entra** on the Entra audience under **Workspace → Workload identity**, and the platform manages the agent identities for you:

* **Created** when an agent is created, under the audience's blueprint, named after the agent, with an optional prefix such as "Abundly:" so your agents are easy to spot in the directory. If Entra can't be reached at that moment, the identity is created on the agent's first Entra call, or with the **Sync** button on the agent's Settings page.
* **Renamed** when the agent is renamed.
* **Deleted** when the agent is permanently deleted, which is after the grace period that follows a delete. Entra keeps a deleted identity restorable for 30 days.
* **Never touched** when a workspace admin pasted it by hand.

Turning autoprovisioning off later changes nothing in Entra: the identities stay and keep working, and new agents then need one pasted by hand. Deleting the audience from Abundly removes the mapping from every agent; the confirmation lets you choose whether the identities the platform created are deleted in Entra as well.

What it needs in your directory:

* A **sponsor group**: every agent identity must have an accountable [sponsor](https://learn.microsoft.com/en-us/entra/agent-id/agent-owners-sponsors-managers). Enter the object ID of a Microsoft 365 or dynamic membership group; security groups and role-assignable groups aren't accepted. The user who created the agent is added as a sponsor too, when they signed in to Abundly with a Microsoft account in that directory.
* The blueprint's **`AgentIdentity.CreateAsManager`** permission, which a blueprint holds by default. It lets the blueprint create and delete agent identities under itself and nothing else.
* **Access** for the identities: assign app roles per agent, or grant a baseline to all of them through the blueprint's inheritable permissions.

The agent's Settings page shows the identity's application ID and a **Sync** button; everything else happens in the background.

### Check that it works

In the Microsoft Entra admin center, the identities are listed under **Agent ID**, with the display name you chose, so a prefix such as "Abundly:" makes them easy to find. The quickest end-to-end check of the trust is the **Sync** button on an agent's Settings page when autoprovisioning is on: it creates the identity through the blueprint's federated credential, and shows Entra's own message if the credential or the blueprint's permission is wrong. The MCP server connect dialog exercises the full token exchange for one agent, including the roles you assigned.

### If it doesn't work

| Message | What to check |
| - | - |
| `AADSTS700212`, or "no matching federated identity record" | The blueprint's federated credential: the issuer is exactly the Issuer URL, the subject is `workspace:<workspace ID>` (not the agent's subject), the audience is `api://AzureADTokenExchange`. |
| `AADSTS65001`, or a consent error on the agent identity request | The identity has no app role for the requested scope's resource. Assign the role, or `Sites.Selected`, and grant admin consent. |
| "No Microsoft Entra agent identity is set for this agent" | The mapping on the agent's Settings page is empty for that audience. Paste the identity's application ID, or click **Create in Entra** when the audience autoprovisions. |
| "Microsoft Entra rejected the agent identity request" (`Authorization_RequestDenied`) | Autoprovisioning: the blueprint lacks `AgentIdentity.CreateAsManager`, or the sponsor group is of a type Entra doesn't accept. |

## Use it with an Outgoing API

Add a keyhole of type **Workload identity (keyless)** to the [Outgoing API](https://docs.abundly.ai/features/http.md#handling-credentials) and pick an audience. There's no key to choose when you enable the API on an agent: every agent authenticates as itself. The API's settings on each agent show what to grant or check for that agent, its team and the whole workspace.

* **Google APIs:** pick a Google Cloud audience. The API's endpoints must be on `googleapis.com`: the agent sends Google's access token, which is only ever sent to Google. The scope defaults to `https://www.googleapis.com/auth/cloud-platform`. Enter a service account to impersonate, or leave it empty and grant roles to the agent's principal directly, as in step 3 above.
* **Microsoft Graph or an Entra-protected API:** pick a Microsoft Entra audience and enter the scope, for example `https://graph.microsoft.com/.default` or `api://<app id>/.default`. A token for an `https://` resource is only ever sent to that host.
* **Your own API or gateway:** add a Direct audience, for example your API's URL, and pick it. The agent sends its token in the `Authorization: Bearer` header. Your API should verify:
  * the signature, with the keys published at `<issuer URL>/.well-known/openid-configuration`
  * `iss` is your issuer URL, `aud` is the audience you added, and the token hasn't expired
  * `workspace_id` is your workspace ID, plus any team or agent you want to allow

## Use it with an MCP server

When connecting an [MCP server](https://docs.abundly.ai/integrations/mcp.md), the authentication dialog offers a **Workload identity** tab next to OAuth and API token. Pick an audience: a Direct audience sends the agent's own token for your server to verify, a Microsoft Entra audience sends an Entra token for the scope you enter, and a Google Cloud audience works for servers on `googleapis.com`. Nothing is stored for the server; a token is minted for every run. A Microsoft Entra audience can be chosen by workspace admins only, because it decides where the agent's identity token is sent; Google Cloud and Direct audiences take Edit access to the agent.

## Good to know

* An agent's subject includes its team, so moving an agent to another team changes its identity. Team-based grants follow the agent automatically; grants on the old subject stop working. Entra agent identities are mapped per agent and are unaffected.
* Tokens are valid for five minutes. The platform sends a Google Cloud or Microsoft Entra audience's token only to that cloud's token service, and a Direct audience's token only to the MCP server or Outgoing API that uses it. They are never shown to the agent's model.
* For Microsoft Entra, the token's `sub` is `workspace:<workspace ID>` rather than the agent's subject, because Entra's federated credential matches the subject exactly. The other claims are unchanged.

## FAQ

> **What can Abundly do in my Entra directory?**
>
> Nothing, until you turn on autoprovisioning. With it on, the platform acts as the blueprint, whose `AgentIdentity.CreateAsManager` permission allows exactly one thing: creating, renaming and deleting agent identities under that blueprint. It can't read your directory, assign permissions or touch any other object. What an agent can reach is what you assign to its identity.

> **Can one blueprint serve several Abundly workspaces?**
>
> Entra allows it, with one federated credential per workspace subject, but don't: any credential on a blueprint can request tokens for any agent identity under it, so workspaces sharing a blueprint could act as each other's agents. Give each workspace its own blueprint.

> **Who decides which identity an agent uses, and where its token goes?**
>
> Workspace admins map the agent identity to the agent on its Settings page, and only they can connect an MCP server or an Outgoing API with a Microsoft Entra audience, because that decides where the agent identity's token is sent. Google Cloud and Direct audiences stay with whoever may edit the agent or API: their tokens are only good at Google or at the audience itself. A token for an `https://` resource such as Microsoft Graph only ever goes to that host, whoever configured it.

> **Is there anything to rotate or anything that could leak?**
>
> No secret exists. Abundly keeps the directory ID, the blueprint's application ID and each agent's identity ID, which are public identifiers, and Entra holds the trust in the federated credential. Tokens are minted per run, are short-lived and are never shown to the agent's model.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.