Agent directories Module
What this does — and what it does not
Vaks PM treats AI agents as first-class actors: an agent is a user account with kind = AGENT, a mandatory human owner, a trust level, and an explicit list of capabilities. A directory connector saves you from creating those accounts by hand when your organization already declares its agents somewhere else. It reads the agent list from that platform on demand and mirrors it.
Being precise about the boundaries matters here, because this integration is deliberately narrow:
- It imports identities, not permissions. A synced agent arrives at trust level Draft only with a single capability,
read:project. Nothing on the remote platform can widen that. - It never mints credentials. A synced agent has no API token and cannot sign in at all. Issuing a token is a separate, deliberate human action (see step 4).
- It never deletes. An agent disabled on the platform becomes Suspended in Vaks PM; deleting the connector leaves the already-imported agents in place.
- It is manual. There is no background scheduler. The mirror only advances when someone presses Sync now.
- It is one-way. Vaks PM never writes back to Entra.
On re-sync, only the agent's name, status and model fields are refreshed. Trust level, capabilities and owner — everything a human decided — are never overwritten.
Prerequisites
You need rights on both sides, and they are usually held by two different people. Confirm both before starting. The far side depends on the platform you are connecting; the row below is for Microsoft Entra, the provider available today — another provider would ask for its own service credentials in place of an app registration.
| Side | What you need | Why |
|---|---|---|
| The source directory (Microsoft Entra today) |
For Entra: permission to create an app registration, plus a Privileged Role Administrator or Global Administrator to grant admin consent | The connector reads the platform's agent identities with its own service credentials. For Entra this is an application permission, which only takes effect once an administrator consents on behalf of the whole tenant. |
| Vaks PM | The agent:manage permission — held by any organization administrator, or by a user granted the orthogonal AI administrator role |
All agent and connector screens sit behind this single permission. It can never be held by an agent itself. |
| Vaks PM | At least one active human user to designate as default owner | Every agent must be owned by an active human. Entra does not expose an owner for agent identities, so without a default owner every agent is skipped. |
Step 1 — In Microsoft Entra
You are creating a dedicated app registration whose only job is to read the agent list. It authenticates as itself — no user is involved, no one signs in — using the OAuth 2.0 client-credentials flow.
1.1 Create the app registration
Navigate to Microsoft Entra admin center → Identity → Applications → App registrations → New registration.
- Name: something that makes its purpose obvious to whoever audits it later, for example
Vaks PM — agent directory sync. - Supported account types: single tenant (accounts in this organizational directory only).
- Redirect URI: leave empty. This application never receives a browser redirect.
After registration, copy two values from the Overview page — you will paste both into Vaks PM:
- Directory (tenant) ID → the Tenant ID field
- Application (client) ID → the Client ID field
1.2 Grant the Graph permission
Go to API permissions → Add a permission → Microsoft Graph → Application permissions, and add exactly one:
AgentIdentity.Read.All
Then press Grant admin consent for <tenant> and confirm that the permission's status column reads Granted. This is the step most often missed: without consent the permission is listed but inert, and the connector's Test button will fail with an explicit 403.
AgentIdentity.Read.All is read-only and scoped to agent identities — it grants no access to users, groups, mail or files. This is the entire Graph surface the integration touches.
1.3 Create a client secret
Go to Certificates & secrets → Client secrets → New client secret, choose an expiry that fits your rotation policy, and copy the secret Value immediately — Entra shows it once and never again.
1.4 Alternative — certificate authentication (no secret, nothing to watch for expiry)
Instead of the client secret from step 1.3, this app registration can authenticate with a certificate (private_key_jwt) — see the detailed procedure in the SSO OIDC guide (generating the key/certificate pair, uploading the .cer, the thumbprint). It is the same Entra app as in step 1.1: one certificate is enough if you reuse this app for other Vaks PM Entra integrations.
Step 2 — In Vaks PM
Open Admin → Integrations → Agent directories and press Add directory. Choose Microsoft Entra Agent ID as the platform, then fill in the form:
| Field | Value |
|---|---|
| Label | Free text, shown in the connector list. Useful when you run more than one. |
| Tenant ID (Entra) | The Directory (tenant) ID GUID from step 1.1. |
| Client ID (app registration) | The Application (client) ID GUID from step 1.1. |
| Client secret or certificate | The secret Value from step 1.3, or the thumbprint + private key if using certificate authentication (step 1.4). Encrypted at rest (AES-256-GCM) and never displayed again. Leave blank when editing to keep what is stored; a private key on file takes precedence over the secret. |
| Default owner for imported agents | An active human who becomes the owner of every imported agent. Required in practice for Entra — see the warning below. |
| Enable this connector | Tick it. Sync now stays greyed out until the connector is enabled and saved. |
Two constraints worth knowing before you commit: the platform choice is immutable after creation — switching means deleting and recreating — and nothing stops you creating several connectors for the same platform, so check the list before adding a duplicate.
Step 3 — Test, then sync
Press Test on the connector card. This acquires a token and makes a single, minimal Graph call. It changes nothing and persists nothing — run it as often as you like.
- Expected result: the toast Connection OK.
- Anything else means the Entra side is not ready. Match the message against Troubleshooting before continuing — syncing will not fix it.
Once the test passes, press Sync now. The connector enumerates the tenant's agent identities and mirrors them.
- Expected result: Sync done: N created, 0 updated, 0 suspended on the first run, where N matches the number of agents in your tenant.
- The card then shows Last sync with a timestamp. A failure is recorded too, as a Last sync failed pill with the reason appended.
- Re-running is safe. Agents are matched on their Entra object ID, so a second sync updates rather than duplicates.
Then confirm the result where it counts: Admin → Users & Identity → Agents should now list the imported agents, each showing its owner and the Draft only trust level.
Step 4 — Activate an agent
An imported agent exists but is inert. It has no token, cannot sign in, cannot be granted a local password, holds no licence seat, and carries a single capability: read:project. This is deliberate — the import is a governance step, not an authorization step, and nothing in Entra can shortcut it.
To put an agent to work, open Admin → Users & Identity → Agents, select it, and:
- Confirm the owner. The default owner from the connector applies to every agent it imported; reassign per agent so accountability lands on the person who actually operates it.
- Set the trust level and capabilities to the minimum the agent needs. Capabilities are a hard ceiling: an agent's effective rights are its role's permissions intersected with its token's scopes intersected with its capabilities. Finance, client data, administration and time logging can never be granted to an agent, whatever you tick.
- Issue an API token. The secret is displayed exactly once — capture it at that moment. Expiry is mandatory, defaulting to 90 days.
From that point the agent's actions are attributed to it in the audit log as AGENT, alongside its owner's identity, so every action remains traceable to a responsible human.
Alternative — inbound registration
If your agent platform is not Entra, or you would rather have the platform announce its agents as it provisions them, use the push model instead. It needs no app registration.
In Admin → Integrations → Agent directories, under Inbound — a platform declares its agents, use the Agent registration API card to mint an organization API key. The key is automatically scoped to agent:manage and nothing else; an expiry is mandatory (default one year, maximum two).
Your platform then calls:
POST /api/v1/agents
Authorization: Bearer vaks_org_<your-key>
Content-Type: application/json
{
"name": "Docs drafting agent",
"externalId": "agent-8842",
"ownerEmail": "owner@example.com",
"modelProvider": "anthropic",
"model": "claude-opus-4-8",
"hostingRegion": "eu-west"
}
name, externalId and ownerEmail are required; the last three fields are optional descriptive metadata. The call is idempotent on externalId, so re-registering the same agent updates it rather than creating a duplicate — the response returns created: true or false so the caller can tell which happened.
ownerEmail must match an active human in the organization, or the call is rejected with 400. As with the connector, the agent arrives at Draft only with read:project and no token: a human still has to activate it.
model and modelProvider fields are descriptive metadata only — they carry no pricing. What an agent costs is set separately, either in Admin → Agents or by API (below), and what it consumes is declared per run. The full lifecycle is: register (identity) → set the cost → activate (a human issues the token) → run and report cost.
Setting an agent's internal cost by API
You can keep an agent's internal token cost in step with your provider's pricing from the same integration, without opening the admin panel. Using an organization key scoped agent:manage — the same kind of key as the registration above:
PATCH /api/v1/agents/cost-profile
Authorization: Bearer vaks_org_<your-key>
Content-Type: application/json
{
"externalId": "agent-8842",
"costRateInPerKTokens": 0.006,
"costRateOutPerKTokens": 0.03
}
The agent is found by externalId, the same key as registration. Both rates are in currency per 1000 tokens; at least one is required, and null clears a rate. The response is the updated agent, and the change is written to the audit log with the old and new values.
defaultBillRatePerKTokens) is deliberately not accepted here: an organization key cannot set your selling price. That stays in Admin → Agents, under finance control. If the same externalId exists on more than one source, the call returns 409 — add idpSource to disambiguate.
Troubleshooting
Messages are shown in your interface language. They are reproduced below in English so you can match them exactly.
| Message | Cause & fix |
|---|---|
| Access denied (403) — the AgentIdentity.Read.All application permission is missing (admin consent required). | The permission was added but never consented, or was consented as a delegated rather than application permission. Return to step 1.2 and confirm the status column reads Granted. |
| Entra authentication refused: … | Tenant ID, client ID or client secret is wrong, or the secret has expired. The trailing text is Microsoft's own explanation and usually names which. Expired secrets are the most common cause on a connector that used to work. |
| Incomplete configuration (tenantId / clientId / clientSecret). | A field was left empty on save. It can also mean the stored secret has become unreadable after an encryption-key rotation — in that case simply re-enter the secret and save. |
| Connector disabled — enable it before syncing. | Tick Enable this connector and save. Test works on a disabled connector; Sync now does not. |
| Timed out reaching Microsoft. | Graph did not answer within 10 seconds. Usually transient — retry. If it persists, check outbound network access and any egress proxy between the instance and graph.microsoft.com. |
| Unexpected Graph response (HTTP n). / Graph enumeration failed (HTTP n). | Graph answered with an unexpected status. A 429 means throttling — wait and retry. Anything else is worth raising with support along with the connector's Last sync line. |
| Sync reports Nothing imported: N agent(s) skipped because no owner could be resolved | The default owner is missing: every agent was skipped for lack of one. Set it and re-sync. |
| Sync reports 0 created, 0 updated, 0 suspended with nothing skipped | The tenant genuinely holds no agent identities — confirm with Test, which succeeds either way. |
| defaultOwnerId invalide : humain ACTIF requis. | The chosen owner is suspended, deleted, or is itself an agent. Pick an active human. |
What is logged
Every step here leaves an audit trail, readable under Admin → Security & Compliance → Audit log and exportable to your SIEM:
| Action | Recorded |
|---|---|
| Connector created, updated, deleted | Actor and target connector. Secrets are redacted — the client secret never reaches the audit log. |
| Sync | Platform, plus counts of agents created, updated, suspended and skipped. The on-screen message reports the skipped count too; the audit log keeps it after the toast is gone. |
| Agent lifecycle | Creation, suspension, capability and trust-level changes, token minting and revocation — all at critical severity. |
| Agent activity | Every action an agent takes is attributed to actor type AGENT with its owner's identity attached, and can be filtered on that basis. |
Related: all integrations · product & features for the agent governance model · encryption & keys for how connector secrets are protected at rest.