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