Azure APIM Agent Onboarding
Azure API Management Agent Onboarding — automated agent self-registration on Microsoft Azure APIM. Uses the Product → Subscription → User nested scope model (the cleanest gateway-native scope abstraction in the field), Microsoft Entra service principal short-circuit for first-party agents, Azure Monitor for audit (Tier 2 side-channel — Application Insights custom dimensions carry the Web Bot Auth signature). Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko's governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.
What You Can Do
MCP Tools
agent-register
agent-revoke
Capability Spec
naftiko: 1.0.0-alpha2
info:
label: Azure APIM Agent Onboarding
description: 'Azure API Management Agent Onboarding — automated agent self-registration
on Microsoft Azure APIM. Uses the Product → Subscription → User nested scope model
(the cleanest gateway-native scope abstraction in the field), Microsoft Entra service
principal short-circuit for first-party agents, Azure Monitor for audit (Tier 2
side-channel — Application Insights custom dimensions carry the Web Bot Auth signature). Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko''s governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.'
tags:
- Microsoft Azure
- Azure APIM
- Microsoft Entra
- Agent Onboarding
- Products
- Subscriptions
- Azure Monitor
- Application Insights
- Naftiko Capability
created: '2026-05-28'
modified: '2026-05-28'
related:
- https://apievangelist.com/2026/05/27/automated-agent-onboarding-is-a-naftiko-capability-not-a-gateway-feature/
- https://github.com/api-evangelist/microsoft-azure-api-management
binds:
- namespace: env
keys:
AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_ID
AZURE_RG_NAME: AZURE_RG_NAME
AZURE_APIM_SERVICE_NAME: AZURE_APIM_SERVICE_NAME
AZURE_BEARER_TOKEN: AZURE_BEARER_TOKEN
APPINSIGHTS_WORKSPACE_ID: APPINSIGHTS_WORKSPACE_ID
AGENT_TRUSTED_ISSUERS: AGENT_TRUSTED_ISSUERS
AGENT_TRUSTED_ENTRA_PRINCIPALS: AGENT_TRUSTED_ENTRA_PRINCIPALS
AGENT_CONSENT_HASH: AGENT_CONSENT_HASH
capability:
consumes:
- type: http
namespace: azure-apim
baseUri: https://management.azure.com
description: Azure APIM Management API. Uses the standard
/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{svc}
base path with the api-version=2023-09-01-preview query parameter.
resources:
- name: apim-users
path: /subscriptions/{azureSubId}/resourceGroups/{rgName}/providers/Microsoft.ApiManagement/service/{serviceName}/users/{userId}
operations:
- name: createorupdateuser
method: PUT
description: Create a User entity (the agent's identity in APIM).
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
inputParameters:
- { name: body, in: body, type: object, required: true }
- name: apim-products
path: /subscriptions/{azureSubId}/resourceGroups/{rgName}/providers/Microsoft.ApiManagement/service/{serviceName}/products
operations:
- name: listproducts
method: GET
description: List existing Products to resolve the scope-tier name.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
- name: createorupdateproduct
method: PUT
description: Create a Product if the scope tier is missing. Product carries the
quota, rate limit, and which APIs are accessible.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
- name: apim-subscriptions
path: /subscriptions/{azureSubId}/resourceGroups/{rgName}/providers/Microsoft.ApiManagement/service/{serviceName}/subscriptions/{sid}
operations:
- name: createorupdatesubscription
method: PUT
description: Create the Subscription that grants the User access to the Product.
The subscription holds two keys (primary + secondary); rotating one captures the
credential value.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
inputParameters:
- { name: body, in: body, type: object, required: true }
- name: deletesubscription
method: DELETE
description: Revoke by deleting the Subscription.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
- name: apim-subscriptions-listSecrets
path: /subscriptions/{azureSubId}/resourceGroups/{rgName}/providers/Microsoft.ApiManagement/service/{serviceName}/subscriptions/{sid}/listSecrets
operations:
- name: listsubscriptionsecrets
method: POST
description: Retrieve the subscription's primary + secondary key values.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
authentication:
type: bearer
token: '{{env.AZURE_BEARER_TOKEN}}'
- type: http
namespace: azure-application-insights
baseUri: https://api.applicationinsights.io/v1
description: Application Insights / Log Analytics — audit side-channel where Azure
APIM diagnostic logs land. Custom dimensions on APIM requests carry the Web Bot Auth
signature and consent hash for queryable observability.
resources:
- name: workspace-query
path: /workspaces/{workspaceId}/query
operations:
- name: querylogs
method: POST
description: Kusto query against the APIM logs surface for audit observation.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
inputParameters:
- { name: workspaceId, in: path, type: string, required: true }
- { name: body, in: body, type: object, required: true }
authentication:
type: bearer
token: '{{env.AZURE_BEARER_TOKEN}}'
orchestration:
- name: onboard-agent
description: Verify identity (Web Bot Auth or Entra service principal), ensure
Product, create User + Subscription, capture key from listSecrets, observe audit
via App Insights.
inputs:
- { name: signature, type: object, required: false, description: 'Web Bot Auth signature for third-party agents.' }
- { name: signature_agent, type: string, required: false }
- { name: entra_principal, type: string, required: false, description: 'Microsoft Entra service principal object ID for first-party agents.' }
- { name: skill_id, type: string, required: true }
- { name: requested_scopes, type: array, required: true }
- { name: consent_hash, type: string, required: true }
- { name: contact, type: object, required: true }
steps:
- id: verify_identity
type: builtin.identity.resolve
description: Prefer Entra principal short-circuit for in-tenant first-party agents;
fall back to Web Bot Auth for third-party.
with:
entra_principal: '${input.entra_principal}'
trusted_entra_principals: '{{env.AGENT_TRUSTED_ENTRA_PRINCIPALS}}'
signature: '${input.signature}'
signature_agent: '${input.signature_agent}'
trusted_issuers: '{{env.AGENT_TRUSTED_ISSUERS}}'
on_failure: deny
- id: verify_consent
type: builtin.policy.assert
with:
assert: '${input.consent_hash} == {{env.AGENT_CONSENT_HASH}}'
on_failure: deny
- id: classify_scopes
type: builtin.policy.scope-classify
with:
requested: '${input.requested_scopes}'
- id: ensure_product
type: builtin.upsert
description: Ensure the scope-tier Product exists.
with:
list_call: azure-apim.listproducts
filter: 'name == "${steps.classify_scopes.target}"'
create_call: azure-apim.createorupdateproduct
create_with:
body:
properties:
displayName: '${steps.classify_scopes.target}'
subscriptionRequired: true
approvalRequired: false
state: published
- id: create_user
call: azure-apim.createorupdateuser
with:
userId: 'agent-${steps.verify_identity.agent_id}'
body:
properties:
firstName: Agent
lastName: '${steps.verify_identity.agent_id}'
email: 'agent-${steps.verify_identity.agent_id}@${input.contact.operator}'
state: active
note: 'Auto-onboarded by Naftiko capability for ${input.contact.operator}'
- id: create_subscription
call: azure-apim.createorupdatesubscription
with:
sid: 'agent-sub-${steps.verify_identity.agent_id}'
body:
properties:
displayName: 'Agent ${steps.verify_identity.agent_id}'
scope: '${steps.ensure_product.id}'
ownerId: '${steps.create_user.id}'
state: active
- id: capture_credential
call: azure-apim.listsubscriptionsecrets
description: listSecrets is the only retrieval path for the subscription's primary key.
with:
sid: '${steps.create_subscription.name}'
- id: emit_audit
description: Azure Monitor captures the APIM management ops automatically;
the orchestration writes a corresponding Application Insights custom event
with the agent identity + Web Bot Auth signature + consent hash so the worker
can query a single log surface.
type: builtin.audit.emit
with:
target: azure-application-insights.querylogs
custom_event:
event_type: agent.onboarded
agent_id: '${steps.verify_identity.agent_id}'
trust_mode: '${steps.verify_identity.trust_mode}'
operator: '${input.contact.operator}'
skill_id: '${input.skill_id}'
scope_tier: '${steps.classify_scopes.target}'
consent_hash: '${input.consent_hash}'
subscription_id: '${steps.create_subscription.id}'
output:
agent_id: '${steps.verify_identity.agent_id}'
trust_mode: '${steps.verify_identity.trust_mode}'
user_id: '${steps.create_user.id}'
subscription_id: '${steps.create_subscription.id}'
credential:
type: SubscriptionKey
header: Ocp-Apim-Subscription-Key
value: '${steps.capture_credential.primaryKey}'
revocation_url: '/v1/agents/${steps.verify_identity.agent_id}/revoke'
scope_tier: '${steps.classify_scopes.target}'
- name: revoke-agent
description: Delete the Subscription; the User entity persists for audit purposes.
inputs:
- { name: subscription_id, type: string, required: true }
steps:
- id: delete_sub
call: azure-apim.deletesubscription
with:
sid: '${input.subscription_id}'
output:
revoked: true
exposes:
- type: rest
namespace: azure-apim-agent-onboarding-rest
port: 8080
resources:
- path: /v1/agents/onboard
operations:
- { method: POST, name: onboardagent, call: orchestration.onboard-agent }
- path: /v1/agents/{agent_id}/revoke
operations:
- { method: POST, name: revokeagent, call: orchestration.revoke-agent }
- type: mcp
namespace: azure-apim-agent-onboarding-mcp
port: 9090
transport: http
tools:
- name: agent-register
hints: { readOnly: false, destructive: false, idempotent: false }
call: orchestration.onboard-agent
- name: agent-revoke
hints: { readOnly: false, destructive: true, idempotent: true }
call: orchestration.revoke-agent
- type: agent-skill
namespace: azure-apim-agent-onboarding-skill
description: 'Agent skill at /skills/onboard-agent.md. Azure-specific addendum: agents
running in-tenant (Azure Functions, App Service, Container Apps, Bedrock-equivalents)
can present a Microsoft Entra service principal token and skip Web Bot Auth.'
skill:
name: onboard-agent
file: skills/onboard-agent.md