Agent Identities
An agent identity is a non-human principal owned by exactly one team. Register one for an AI agent or an automation that runs without a person signed in, such as a scheduled job or a team assistant. The agent authenticates with its own credentials, carries its own permissions, and is the consumer of its own access agreements.
Agents and people
An agent identity is not a user. It can't sign in to the web UI, takes no seat, and is no member of any team. Its owning team is accountable for it.
An AI client that works for a signed-in person, such as Claude, Copilot, or ChatGPT connected through MCP OAuth or a personal API key, keeps that person's identity and needs no agent identity. Entropy Data records the AI client next to the person. An access agreement requested through MCP OAuth shows, for example, Requested by: juliet@example.com via AI client, and its audit trail names the client: Juliet Peers via Claude.
Register an agent identity when the agent runs on its own, so that its access belongs to the agent and does not depend on any one person.
Register an agent
Open the team page in Studio and choose Add Agent in the Agents section.
| Field | Description |
|---|---|
| Name | The display name of the agent. |
| ID | Unique in the organization. Used in the API and in the audit log as agent:<id>. It can't be changed later. |
| Description | Optional. What the agent is. |
| Purpose | Required. What the agent does with data. It pre-fills access requests for the agent and, with AI features enabled, is checked against the terms of use of data contracts. |
| Linked identities | Optional. The same agent in other systems, each as a type and an identifier. Provisioning uses them to grant access in the data platform, and tokens from your identity provider are matched against them. |
| Permissions | What the agent may change, see Permissions. |
The web UI offers these linked identity types. Through the API you can also use a type of your own.
| Type | API value |
|---|---|
| Microsoft Entra Agent ID | entra-agent-id |
| Okta | okta |
| Snowflake user | snowflake-user |
| Databricks service principal | databricks-service-principal |
| Google Cloud service account | gcp-service-account |
| AWS IAM role | aws-iam-role |
| SPIFFE ID | spiffe-id |
| Other | other |
Members who hold TEAM_API_KEY_MANAGE on the owning team register and edit its agents, manage their credentials, and set their permissions. Organization owners can do the same for any team. Through the API, any API key that carries TEAM_API_KEY_MANAGE for the owning team can, such as an organization API key or a key of that team. Either way, you can only grant permissions you hold on that team yourself. When editing an agent, you can keep permissions someone else granted it, but not add ones you don't hold.
Through the API
PUT /api/agent-identities/{id} registers or updates an agent and replaces the whole record, so a field you leave out, such as description or linkedIdentities, is cleared. owner is the id of the owning team. If you omit permissions, the agent gets ACCESS_REQUEST alone, on update as well as on create. Pass an empty list for a read-only agent. status is active or disabled.
curl --request PUT https://api.entropy-data.com/api/agent-identities/churn-risk-scorer \
--header "x-api-key: $ENTROPY_DATA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"id": "churn-risk-scorer",
"name": "Churn Risk Scorer",
"owner": "sales-team",
"description": "Scores active customers for churn risk every night.",
"purpose": "Predict churn risk for active B2B customers so account managers can prioritize retention calls.",
"permissions": ["ACCESS_REQUEST"],
"linkedIdentities": [
{ "type": "snowflake-user", "id": "SVC_CHURN_SCORER" }
]
}'
Changing owner moves the agent to another team, which takes TEAM_API_KEY_MANAGE on both teams. GET /api/agent-identities lists the agents of the teams your key can read, and ?owner=<team id> narrows the list to one team.
Permissions
An agent's permissions come from the same team permission catalog as an API key's. They apply to the owning team and its subteams.
ACCESS_REQUESTis preselected, so a new agent can request access for itself and change nothing else.- Read only is an empty list. The agent reads what it can see and changes nothing. It can still query data it has access to.
- All gives the agent every permission you can grant in this team, as they are when you save. Later changes to your own role don't carry over. Restricted picks permissions one by one, from those you hold on the team.
- These permissions are never available to agents:
TEAM_ADD,TEAM_EDIT,TEAM_DELETE,TEAM_API_KEY_MANAGE,TEAM_MEMBER_ADD,TEAM_MEMBER_EDIT,TEAM_MEMBER_DELETE, andORGANIZATION_ADMIN. An agent can't create credentials, change its team, or change who is accountable for it. ACCESS_APPROVEandACCESS_ADDnever apply to the agent's own agreements.
These rules hold whatever permissions the agent carries:
- An agent requests access only for itself.
- An agent never approves, adds, or extends an agreement in which it is the consumer.
- An agent never changes its own identity, keys, or permissions.
Permissions decide what the agent may change. Which data it may query is decided by its access agreements.
The agent page
Select an agent in the team's Agents section to open its page:
- Info: the purpose, description, linked identities, and who registered the agent and when. The ID is shown under the agent's name, next to the owning team, which answers for the agent, and the agent's status.
- Access: the agreements in which the agent is the consumer.
- API Keys and OAuth Clients: the agent's credentials.
- Data Platform Connections: the datasource connections the agent queries data with.
- Permissions: what the agent may change on its owning team and subteams.
- How to connect: the MCP and token endpoints, with guides for common agent frameworks.
- Activity: what the agent did through MCP and the API. See Activity.
- Audit Trail: what people changed on the agent: its registration, changes, permission changes, disabling and enabling, API keys, OAuth clients, and connections created and deleted, and access requested on the agent's behalf.
Activity
The activity records, newest first:
- every MCP tool call the agent makes, with its arguments
- every API request the agent makes, including requests to endpoints that aren't open to agents
- the access requests the agent makes itself
- every access decision on the agent's queries, allowed or denied, including dry runs
Failed calls are marked Failed, with the error or the HTTP status. An access decision or access request made during a tool call or an API request appears as that call's entry, with the tool and its arguments or the request and its query parameters. If the call fails, it also gets an entry of its own, marked Failed. A decision that another caller asks for about the agent, for example through the access evaluation API, names that caller.
Each agent keeps its newest 10,000 entries from the last 30 days. Older entries are deleted when the agent's next entry is recorded. Show older loads earlier entries.
Select an entry to open its page: the tool and its arguments (long values shortened), or the API request and its query parameters, the outcome and duration, the API key or OAuth client and the client that made the call, and the other calls of the same session.
Sessions
An entry's Session groups the calls of one run of the agent. Select it to show only that session's calls. Clients send different identifiers, so Entropy Data takes the first one a call carries. API requests use only the X-Agent-Session-Id and traceparent headers.
| Source | Sent by |
|---|---|
X-Agent-Session-Id header | Your agent, with an ID of your choice, such as its own run or conversation ID. |
| OpenAI conversation | ChatGPT, as _meta["openai/session"]. |
| Session of the client | Clients such as Codex, as _meta.sessionId. |
| Trace ID of the client | Clients that send W3C trace context, in _meta.traceparent or the traceparent header. |
| MCP session | Any MCP client. Some clients open one per tool call, others keep one for as long as they run. |
To group an agent's calls reliably, send X-Agent-Session-Id with every MCP and API request of a run:
OpenAI Agents SDK
import os
import uuid
from agents.mcp import MCPServerStreamableHttp
async def main() -> None:
async with MCPServerStreamableHttp(
name="Entropy Data",
params={
"url": "https://app.entropy-data.com/mcp",
"headers": {
"Authorization": f"Bearer {os.environ['ENTROPY_DATA_AGENT_KEY']}",
"X-Agent-Session-Id": str(uuid.uuid4()),
},
},
) as server:
... # pass the server to your agent, e.g. Agent(mcp_servers=[server])
Disable or delete an agent
- Disable stops the agent at once: its API keys and access tokens no longer work. Its agreements stay as they are. Enable turns it back on.
- Delete revokes the agent's API keys and OAuth clients, removes its connections, activity, and audit trail, withdraws the access it has requested, and ends the access it has been granted, after the data contract's notice period if it has one. Providers are notified as for any ended agreement.
A team that owns agents can't be deleted. Delete its agents first, or move them to another team by changing owner through the API.
Credentials
An agent authenticates with an API key, with OAuth client credentials, or with a token from your identity provider. Each of them acts as the agent, with the agent's permissions.
API keys
Create a key on the agent page under API Keys, or with POST /api/agent-identities/{id}/api-keys. An expiration date is required.
curl https://api.entropy-data.com/api/agent-identities/churn-risk-scorer/api-keys \
--header "x-api-key: $ENTROPY_DATA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"displayName": "nightly-run",
"expiresAt": "2027-10-01T00:00:00Z"
}'
The key starts with ed_<environment>_agent_, for example ed_live_agent_. It is shown once, in the response that creates it, and Entropy Data stores only a hash. Send it in the x-api-key header, or as Authorization: Bearer, to /api and to /mcp. MCP clients configure it like any other API key.
A key carries the agent's permissions and has none of its own, so a change to the agent's permissions applies to all of its keys. DELETE /api/agent-identities/{id}/api-keys/{keyId} deletes a key, and it stops working at once.
Select a key on the agent page to open its page: who created it, when it expires and was last used, and the agent's activity with that key.
OAuth client credentials
For an agent that uses the OAuth 2.0 client credentials grant, create an OAuth client on the agent page under OAuth Clients, or with POST /api/agent-identities/{id}/oauth-clients. The response returns clientId and clientSecret. The secret is shown only once.
curl https://api.entropy-data.com/api/agent-identities/churn-risk-scorer/oauth-clients \
--header "x-api-key: $ENTROPY_DATA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"displayName": "scheduler"
}'
The agent exchanges them for an access token at the token endpoint, with HTTP Basic client authentication. The token is valid for one hour. On self-hosted installations, use your own host.
curl https://app.entropy-data.com/oauth2/token \
--user "$CLIENT_ID:$CLIENT_SECRET" \
--data "grant_type=client_credentials"
Send the access token as Authorization: Bearer to /mcp or /api. DELETE /api/agent-identities/{id}/oauth-clients/{clientId} deletes a client, and the tokens it already holds stop working at once.
Select a client on the agent page to open its page with the agent's activity with that client's tokens.
Tokens from your identity provider
An agent that has an identity in your identity provider, such as a Microsoft Entra Agent ID, can authenticate with the access tokens that provider issues to it. An organization owner registers the provider as a trusted issuer, with an API key that carries ORGANIZATION_ADMIN:
curl --request PUT https://api.entropy-data.com/api/agent-token-issuers/entra \
--header "x-api-key: $ENTROPY_DATA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"id": "entra",
"issuer": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"audience": "api://entropy-data",
"claim": "oid",
"linkedIdentityType": "entra-agent-id"
}'
| Field | Description |
|---|---|
issuer | The token's iss claim. |
jwksUri | Optional. The location of the issuer's signing keys. If omitted, they are discovered from the issuer's OpenID configuration. |
audience | Optional, but recommended. The required aud claim. Tokens for other audiences are rejected. If omitted, the audience is not checked. |
claim | The claim that identifies the agent, such as oid, azp, or sub. Defaults to sub. |
linkedIdentityType | The linked identity type the claim value must match, such as entra-agent-id. |
A Bearer token on /mcp or /api whose issuer and audience match is validated against the issuer's signing keys. The value of the configured claim is then matched against the organization's agents' linked identities of that type, and a match acts as that agent. A value that matches more than one agent is refused. For the example above, add a linked identity of type Microsoft Entra Agent ID with the agent's object ID to the agent.
GET /api/agent-token-issuers lists the trusted issuers. DELETE /api/agent-token-issuers/{id} removes one, and its tokens are no longer accepted.
Access
An agent holds only its own access agreements. It never inherits the access of its owning team or of the person who registered it.
Request access for an agent
On a data product's output port, choose Request Access and pick Agent as the consumer. Only the active agents of teams where you hold ACCESS_REQUEST are offered, and picking one fills an empty purpose with the agent's purpose. If the request is approved, only the agent gets access. The members of its team don't.
Organization owners can switch the Agent consumer type on or off, like the other consumer types, under Governance & Workflows → Access Management in the organization settings.
The provider approves as usual, see the Access Approval Workflow. The approval box names the agent and its team, for example Agent Churn Risk Scorer of team Sales requests access to data product Customer 360.
The agent requests access itself
An agent that carries ACCESS_REQUEST can request access for itself:
- MCP: the
request_accesstool. - API:
PUT /api/access/{id}withconsumerset to{"agentId": "<agent id>"}or to{}.
curl --request PUT https://api.entropy-data.com/api/access/churn-scorer-customer-360 \
--header "x-api-key: $AGENT_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"id": "churn-scorer-customer-360",
"info": {
"purpose": "Predict churn risk for active B2B customers so account managers can prioritize retention calls."
},
"provider": {
"dataProductId": "customer-360",
"outputPortId": "snowflake"
},
"consumer": {
"agentId": "churn-risk-scorer"
}
}'
If the agent already has an open request or an approved agreement on that output port, no second request is filed. The MCP tool returns the existing agreement. The API answers 200 without creating the new id, so find the agreement with GET /api/access. A PUT to an id that already exists answers 403. A new request follows the usual approval: the data product owner is notified of the request, and the agent's owning team of the decision.
A self-request also needs the Agent consumer type and access requests to be enabled for the organization. Organizations that saved their consumer types before agents existed have Agent switched off until an owner turns it on.
In the API
An agent's agreements carry consumer.agentId. consumer.teamId and consumer.dataProductId are left out on purpose: a provisioning integration that doesn't know agents finds no principal and grants nothing, instead of granting the agent's whole team.
GET /api/access/{id}
{
"id": "churn-scorer-customer-360",
"info": {
"purpose": "Predict churn risk for active B2B customers so account managers can prioritize retention calls.",
"status": "approved",
"active": true
},
"provider": {
"teamId": "customer-team",
"dataProductId": "customer-360",
"outputPortId": "snowflake"
},
"consumer": {
"agentId": "churn-risk-scorer"
}
}
GET /api/access?consumerType=agent lists the agreements with agent consumers. To provision one, read the agent with GET /api/agent-identities/{agentId}, using a key that can read the agent's owning team, and grant to the platform principal named by its linked identity of your platform's type, for example gcp-service-account for BigQuery or snowflake-user for Snowflake.
Use data
An agent sees what a team API key of its owning team sees: public data products, and those its owning team and subteams own or are given visibility to. Through MCP it also sees data products it holds an active agreement on.
MCP tools
Connect the agent to the MCP server with one of its credentials. These tools are open to agents:
| Tool | For an agent |
|---|---|
search | Finds the data products the agent can see. |
fetch | Shows a data product, with the agent's own access status on each output port. |
request_access | Requests access for the agent itself. |
execute_query | Runs a query on an output port the agent has access to, see Query data. |
check_access | Checks whether a query would be allowed, without running it, see Check access. |
semantics_* | Read the organization's semantics. Saving and deleting need SEMANTIC_NAMESPACE_ADD, SEMANTIC_NAMESPACE_EDIT, or SEMANTIC_NAMESPACE_DELETE on the namespace's team. |
The data product and data contract tools (dataproduct_save, dataproduct_schema, datacontract_get, datacontract_save, datacontract_schema, datacontract_test, datacontract_test_results) refuse agent sessions with This tool is not available to agents.
REST endpoints
These endpoints are open to agents:
GET /api/agent-identitiesandGET /api/agent-identities/{id}: the agent's own record.GET /api/accessandGET /api/access/{id}: the agent's own agreements.PUT /api/access/{id}: an access request for the agent itself.POST /api/access/evaluation: an evaluation of the agent's own access, see Access evaluation API.GET /api/dataproductsandGET /api/dataproducts/{id}.GET /api/datacontracts,GET /api/datacontracts/{id}, andGET /api/datacontracts/{id}/datacontract.yaml.GET /api/teamsandGET /api/teams/{id}.POST /api/test-results: withDATACONTRACT_TEST_RESULTS_EDITorDATAPRODUCT_TEST_RESULTS_EDIT, for the owning team and its subteams.
Every other endpoint answers 403 with This endpoint is not available to agents.
Query data
execute_query runs read-only queries, when the organization allows data access. It runs a query only on an output port where the agent has an active agreement, provisioned where your organization requires it. With AI features enabled, the agent's purpose and the purpose given for the query are also checked against the data contract's terms of use. The query uses the roles of the agent's own agreements.
The query runs with the agent's own datasource connection, so the data platform sees the agent's account. Register one on the agent page under Data Platform Connections, or with POST /api/agent-identities/{id}/connections. The credentials are stored encrypted and are never returned. Without a connection for the output port's platform, execute_query explains how to add one.
curl https://api.entropy-data.com/api/agent-identities/churn-risk-scorer/connections \
--header "x-api-key: $ENTROPY_DATA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"type": "snowflake",
"name": "Churn scorer service user",
"account": "acme-xy12345",
"pat": "<programmatic access token>",
"role": "CHURN_SCORER",
"warehouse": "ANALYTICS_WH"
}'
Every connection has a type and a name, plus the fields of its type:
| Type | Fields |
|---|---|
postgres | host, port, database, user, password |
mysql, mariadb, mssql | host, port, user, password |
exasol | host, port, user, password, certificateFingerprint |
trino | serverUrl, catalog, schema, username, password |
databricks | jdbcUrl, catalogPattern, personalAccessToken |
snowflake | account, pat (a programmatic access token), role, warehouse |
GET /api/agent-identities/{id}/connections lists the agent's connections, and DELETE /api/agent-identities/{id}/connections/{connectionId} removes one. Adding and removing connections takes TEAM_API_KEY_MANAGE on the agent's team.
Check access
The MCP tool check_access is a dry run of the same decision for a query on an output port. It takes dataProductId, outputPortId, and query, and runs nothing. The purpose that counts is the agreement's purpose plus the agent's purpose, never free text from the call.
Each dry run is logged in the agent's activity like a real decision. When an agent is denied five times within an hour, by dry runs or real queries, the owning team gets one email.
Access evaluation API
POST /api/access/evaluation answers the same question in the shape of OpenID AuthZEN. With subject.type set to agent, the request is evaluated against the agent's own agreements:
POST /api/access/evaluation
{
"subject": { "type": "agent", "id": "churn-risk-scorer" },
"resource": {
"type": "dataproduct",
"id": "customer-360",
"properties": { "outputPortId": "snowflake" }
},
"action": {
"name": "can_query",
"properties": {
"sql": "SELECT customer_id, churn_signals FROM customer_360.customers",
"purpose": "Score churn risk for active B2B customers."
}
}
}
The response carries decision, and on a denial the reasons in context.reasons. An agent can only evaluate itself. An API key can evaluate the agents of the teams it can read.
See the Agent Identities endpoints in Swagger for the formal definitions.