How to Add ATXP to Semantic Kernel

How to Add ATXP to Semantic Kernel

Semantic Kernel agents make it easy to wire up plugins and let planners sequence tool calls automatically. What they don’t give you is a payment layer. Semantic Kernel agent payments — managing what your agent spends, where, and under what ceiling — don’t exist in SK out of the box. When your agent calls a web search API, an image generation service, or an email provider through a plugin, you’re either hardcoding API keys into each plugin or building credential management yourself. Neither approach scales past a handful of tools. This guide covers ATXP integration from plugin registration through enterprise IAM considerations, with working Python code throughout.

Why Does Semantic Kernel Have a Payment Gap?

Semantic Kernel (23k+ GitHub stars as of Q1 2026, Microsoft’s official enterprise AI SDK) organizes capabilities as plugins — collections of @kernel_function-decorated methods the kernel discovers and invokes. Planners (Sequential, Stepwise, Handlebars) turn natural-language goals into ordered function calls across those plugins.

The plugin model is elegant but payment-agnostic. A web search plugin has its own API key. An email plugin has another. Image generation, another. By the time your agent has five external capabilities, you’re managing five separate billing relationships, five credential rotation schedules, and five dashboards to monitor spend.

This is the API key sprawl problem that exists for every agent framework — but SK’s heavy use in Azure enterprise environments makes it more acute. Managed identities and Azure AD control who can call what, but they don’t control how much an agent can spend or give you a unified spend ledger when something goes wrong.

How ATXP Fits into the Semantic Kernel Agent Payment Model

ATXP replaces per-service API keys with a single payment layer. Your agent gets one identity (agent_id) and one wallet funded with ATXP credits. When a plugin needs to call a paid external service — web search, image generation, email, SMS — it routes through ATXP. ATXP deducts cost from the wallet and returns the result.

From SK’s perspective, ATXP is just another plugin. From a payment perspective, it’s the only credential you manage.

Before ATXPAfter ATXP
Web searchSearch provider API keyATXP key
Image generationOpenAI / Stability keyATXP key
Email deliverySendGrid API keyATXP key
SMSTwilio SID + tokenATXP key
Spend visibility4 separate dashboardsatxp.ai unified view
Credential rotation4 separate rotations1 rotation
Per-run budget capBuild it yourselfATXP wallet limit

The underlying model is IOU credits — think of them like dollars in a Starbucks wallet: spendable only within ATXP’s tool catalog, with a hard limit at whatever balance you’ve funded. See The Three Models for Agent Payments for a full comparison of credit-based, virtual card, and crypto payment approaches.

Setting Up Semantic Kernel Agent Payments With ATXP

Prerequisites: semantic-kernel>=1.0, an ATXP API key and agent_id from atxp.ai, and a funded agent wallet.

Step 1: Create the ATXP Plugin

import httpx
from semantic_kernel.functions import kernel_function


class AtxpPlugin:
    """Single plugin that routes all paid tool calls through ATXP."""

    BASE_URL = "https://api.atxp.ai/v1"

    def __init__(self, api_key: str, agent_id: str):
        self.headers = {"Authorization": f"Bearer {api_key}"}
        self.agent_id = agent_id

    @kernel_function(
        name="web_search",
        description="Search the web for current information on any topic",
    )
    async def web_search(self, query: str) -> str:
        async with httpx.AsyncClient() as client:
            r = await client.post(
                f"{self.BASE_URL}/search",
                headers=self.headers,
                json={"agent_id": self.agent_id, "query": query},
            )
            r.raise_for_status()
            return r.json()["result"]

    @kernel_function(
        name="send_email",
        description="Send an email on behalf of the agent",
    )
    async def send_email(self, to: str, subject: str, body: str) -> str:
        async with httpx.AsyncClient() as client:
            r = await client.post(
                f"{self.BASE_URL}/email/send",
                headers=self.headers,
                json={
                    "agent_id": self.agent_id,
                    "to": to,
                    "subject": subject,
                    "body": body,
                },
            )
            r.raise_for_status()
            return r.json()["message_id"]

    @kernel_function(
        name="generate_image",
        description="Generate an image from a text description, returns a URL",
    )
    async def generate_image(self, prompt: str) -> str:
        async with httpx.AsyncClient() as client:
            r = await client.post(
                f"{self.BASE_URL}/image",
                headers=self.headers,
                json={"agent_id": self.agent_id, "prompt": prompt},
            )
            r.raise_for_status()
            return r.json()["url"]

Step 2: Register the Plugin With the Kernel

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import (
    OpenAIChatCompletion,
    OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.contents import ChatHistory

kernel = Kernel()

kernel.add_service(
    OpenAIChatCompletion(
        service_id="gpt-4o",
        ai_model_id="gpt-4o",
        api_key="YOUR_OPENAI_KEY",
    )
)

# One plugin replaces all per-service credentials
kernel.add_plugin(
    AtxpPlugin(api_key="YOUR_ATXP_KEY", agent_id="agent_prod_01"),
    plugin_name="atxp",
)

Step 3: Invoke With Auto-Invocation

settings = OpenAIChatPromptExecutionSettings(
    function_choice_behavior=FunctionChoiceBehavior.Auto()
)

history = ChatHistory()
history.add_user_message(
    "Research the current state of agent payment protocols "
    "and send a summary to team@company.com"
)

response = await kernel.invoke_prompt(
    prompt="{{$history}}",
    arguments=KernelArguments(settings=settings, history=history),
)

SK’s auto-invocation handles sequencing: it calls web_search, synthesizes results, then calls send_email. Both route through ATXP. You manage one credential; ATXP handles billing across both tool calls.


ATXP gives your Semantic Kernel agent a real wallet with real spend limits — no per-service credentials, no fragmented dashboards. Fund a wallet, register your agent, and get unified visibility over everything it spends. Get started at atxp.ai.


How Does Budget Isolation Work With SK Planners?

Planners create multi-step execution plans. A sequential plan might have six steps; an uncapped agent can rack up costs across all six silently. ATXP gives you three meaningful budget scopes:

Budget ScopeBehaviorWhen to Use
Agent-level (default)Shared wallet across all plansSimple agents, predictable workloads
Per-run limitCeiling per invocation via scoped authorizationUser-facing agents where each request has a cost ceiling
Per-function limitBudget param per kernel_function callHigh-cost tools (image gen, video) where you want hard caps per call
Multi-agentSeparate agent_id per kernelMulti-tenant or team orchestration with isolated wallets

Here’s a per-run budget wrapper — useful when each user request should have a fixed cost ceiling:

import httpx

ATXP_KEY = "YOUR_ATXP_KEY"


async def run_with_budget(kernel: Kernel, goal: str, max_spend_usd: float):
    """Execute a SK plan with a hard ATXP spend ceiling per run."""

    async with httpx.AsyncClient() as client:
        r = await client.post(
            "https://api.atxp.ai/v1/spend-auth",
            headers={"Authorization": f"Bearer {ATXP_KEY}"},
            json={
                "agent_id": "agent_prod_01",
                "limit_usd": max_spend_usd,
                "label": goal[:80],  # Shows up in your ATXP audit log
            },
        )
        auth = r.json()

    # Swap in a scoped agent ID for this run
    kernel.add_plugin(
        AtxpPlugin(api_key=ATXP_KEY, agent_id=auth["scoped_agent_id"]),
        plugin_name="atxp",
    )

    return await kernel.invoke_prompt(goal)

When the scoped wallet hits its limit mid-plan, ATXP returns 402 Payment Required. The plugin raises a KernelFunctionError, which surfaces to the planner as a tool failure. The plan stops rather than overrunning budget silently. Budget exhaustion is recoverable — top up the wallet and rerun the plan.

For a full treatment of how the credit system enforces limits without real-time authorization overhead on every call, see How ATXP’s IOU Model Caps Agent Spending.

Enterprise IAM vs. ATXP — What Each Layer Actually Controls

In Azure environments, Semantic Kernel typically runs under a managed identity. Managed identities and Azure AD RBAC answer the question: can this agent call this service? ATXP answers a different question: how much can this agent spend, and where did the money go?

These are orthogonal controls. You need both.

ConcernAzure Managed Identity / RBACATXP
Proves agent identity✓ (to ATXP’s catalog)
Controls which APIs agent can callScoped to ATXP tools
Spend limits✗ — IAM has no cost model✓ — per-agent wallet with hard caps
Cross-provider unified billing✗ — each vendor bills separately✓ — single ATXP invoice
Cost per tool call / audit trailPartial (Azure Monitor logs calls, not costs)✓ — cost + tool name + timestamp
Credential rotationPer-service, manual or via Key VaultRotate one ATXP key
Multi-tenant spend isolationVia separate identitiesVia separate agent_ids or scoped authorizations

The practical recommendation: keep managed identities for Azure-native services (Azure AI, Azure Storage, Cosmos DB). Add ATXP for external paid tools in your plugins. You don’t choose between them — they solve different problems.

For a deeper treatment of layered agent security, see Financial Zero Trust for AI Agents and How to Build an AI Agent Without API Keys.

Frequently Asked Questions

Does ATXP work with SK’s Azure OpenAI connector?

Yes. ATXP is independent of which AI service powers your kernel. You can use AzureOpenAIChatCompletion, OpenAIChatCompletion, or any other SK connector for the LLM layer — ATXP only handles payments for external tool calls made through plugins. The two layers don’t interact.

How do I track costs per SK plan vs. per user?

Use the label field on spend authorizations (shown in the per-run budget example above). Pass a user ID, session ID, or goal string as the label. ATXP’s dashboard lets you filter spend by label, so you can slice cost per user, per feature, or per plan type. You can also hit the ATXP API directly for programmatic cost reports by label.

What happens if my agent hits its ATXP budget mid-plan?

ATXP returns 402 Payment Required. Your plugin should raise a KernelFunctionError, which surfaces to the planner as a tool failure. SK’s Stepwise planner will attempt to recover or terminate depending on your configuration. The correct pattern: catch the 402 in the plugin, raise with a clear message, and let the planner report failure to the caller. Budget exhaustion is explicitly recoverable — top up the wallet and rerun.

Does this work with SK’s Handlebars planner?

Yes. ATXP functions are registered as standard @kernel_function methods and are discoverable by any SK planner — Sequential, Stepwise, or Handlebars. The planner sees them as regular plugin functions with names, descriptions, and typed parameters. ATXP’s payment handling is invisible to the planner layer.

Can I run multiple ATXP agents from a single SK application?

Yes. Instantiate AtxpPlugin with different agent_id values and register them as separate plugins, or register one plugin per kernel instance. This maps onto SK’s multi-kernel or multi-agent orchestration patterns and gives you isolated wallets per sub-agent, per tenant, or per task type.


Semantic Kernel’s plugin model makes ATXP a natural drop-in: one plugin, one credential, one spend dashboard. The budget scoping model maps cleanly onto SK’s step-based execution — you can set limits at the kernel, plan, or per-function level without modifying planner logic. In Azure enterprise environments, ATXP sits alongside managed identities as the spending control layer that IAM doesn’t provide.

Start with a single AtxpPlugin, get unified visibility into what your agent is spending, and add per-run budget isolation as your usage grows. The pattern scales from a single-agent prototype to multi-kernel orchestration with separate wallets per team or tenant.