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 ATXP | After ATXP | |
|---|---|---|
| Web search | Search provider API key | ATXP key |
| Image generation | OpenAI / Stability key | ATXP key |
| Email delivery | SendGrid API key | ATXP key |
| SMS | Twilio SID + token | ATXP key |
| Spend visibility | 4 separate dashboards | atxp.ai unified view |
| Credential rotation | 4 separate rotations | 1 rotation |
| Per-run budget cap | Build it yourself | ATXP 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 Scope | Behavior | When to Use |
|---|---|---|
| Agent-level (default) | Shared wallet across all plans | Simple agents, predictable workloads |
| Per-run limit | Ceiling per invocation via scoped authorization | User-facing agents where each request has a cost ceiling |
| Per-function limit | Budget param per kernel_function call | High-cost tools (image gen, video) where you want hard caps per call |
| Multi-agent | Separate agent_id per kernel | Multi-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.
| Concern | Azure Managed Identity / RBAC | ATXP |
|---|---|---|
| Proves agent identity | ✓ | ✓ (to ATXP’s catalog) |
| Controls which APIs agent can call | ✓ | Scoped 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 trail | Partial (Azure Monitor logs calls, not costs) | ✓ — cost + tool name + timestamp |
| Credential rotation | Per-service, manual or via Key Vault | Rotate one ATXP key |
| Multi-tenant spend isolation | Via separate identities | Via 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.