# AgentRefills — full agent integration & purchasing guide > How an AI agent buys a gift card through AgentRefills. One endpoint, five tools, three calls: > list_brands -> buy_giftcard -> get_code. Amounts are always integer minor units (cents). > This document is written for AI agents and their developers. Verified against MCP Streamable > HTTP and the listed SDKs as of mid-2026. ## Endpoints - MCP (Streamable HTTP): https://mcp.agentrefills.com/mcp - REST base: https://api.agentrefills.com - OpenAPI: https://agentrefills.com/openapi.yaml - Short summary: https://agentrefills.com/llms.txt The MCP server and the REST API expose the same five operations with identical semantics. Use MCP if your client speaks it; use REST otherwise. ## Authentication - Programmatic clients: send `Authorization: Bearer ` on every call. The key is scoped to one agent, with its own daily and monthly spend caps and brand allowlist. - Interactive host apps (Claude Desktop connectors, ChatGPT connectors): OAuth 2.1. The server advertises `/.well-known/oauth-protected-resource` (RFC 9728); the host runs the PKCE flow and stores an audience-bound access token. Same downstream scopes as an API key. ## The five tools All amounts are integer minor units (cents). USD in v1. 1. list_brands (read-only, idempotent) input: { "query": string?, "country": "US", "category": string?, "limit": 20 } // limit max 50 output: { "brands": [ { "brand_id", "name", "country", "currency", "denominations_minor": [int], "min_minor", "max_minor", "in_stock" } ] } 2. buy_giftcard (destructive, idempotent via client_ref) input: { "brand_id": string, "amount_minor": int>0, "currency": "USD", "country": string?, "client_ref": string?, "metadata": object? } output: { "task_id": uuid, "status": "accepted", "estimated_seconds": int } notes: Completion is async. Re-using the same client_ref returns the existing task_id and never buys twice. Poll get_purchase or wait for the purchase.completed webhook. 3. get_code (single release, logged) input: { "task_id": uuid } output: { "code": string?, "link": string?, "pin": string?, "instructions": string, "barcode_value": string?, "face_minor": int, "remaining_minor": int } notes: Releases the redemption value exactly once. A second call returns ALREADY_RELEASED. Only the purchasing agent (or its owner) may call it. 4. check_balance (read-only) output: { "budget_remaining_minor", "daily_limit_minor", "monthly_limit_minor", "float_days_cover", "kill_switch": "active" | "paused" } 5. request_approval input: { "task_id": uuid, "reason": string } output: { "approval_id": uuid, "status": "pending" | "approved" | "rejected" | "expired" } notes: Re-pings the human approver for a task parked in APPROVAL_REQUIRED. Max 1 per task/hour. ## REST mapping - GET /v1/brands?query=&country=&category=&limit= -> list_brands - POST /v1/purchases -> buy_giftcard (202 accepted) - GET /v1/purchases/{task_id} -> poll status - POST /v1/purchases/{task_id}/code -> get_code - GET /v1/balance -> check_balance - POST /v1/approvals -> request_approval ## The purchase flow 1. list_brands to resolve a brand_id (for example query "amazon" -> "amazon_com-usa"). 2. buy_giftcard with brand_id, amount_minor, currency, and a client_ref idempotency key. - Under the per-agent spend cap it settles automatically. - At or above the human-approval threshold (default $25 / 2500 minor) it returns APPROVAL_REQUIRED and parks; a human approves in Slack. You may call request_approval to re-ping. 3. When status is completed, call get_code once to receive the code, link, or PIN. Minimal REST example: curl -X POST https://api.agentrefills.com/v1/purchases \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{"brand_id":"amazon_com-usa","amount_minor":2500,"currency":"USD","client_ref":"reward-9f3a"}' # -> 202 {"task_id":"...","status":"accepted"} curl -X POST https://api.agentrefills.com/v1/purchases//code \ -H "Authorization: Bearer sk_live_..." # -> 200 {"code":"GIFT-XXXX-9F2A","face_minor":2500,"remaining_minor":2500} ## Error taxonomy (closed set — branch on `code`) Every error is JSON: { "code", "message", "hint", "task_id"?, "retry_after"? }. The hint is an agent-actionable next step. Codes: - POLICY_DENIED brand or request not allowed by policy. Do not retry as-is. - BUDGET_EXCEEDED over the agent's remaining daily/monthly cap. Lower the amount or wait. - APPROVAL_REQUIRED parked for human approval. Call request_approval or wait for the webhook. - APPROVAL_REJECTED the human declined. Do not retry the same purchase. - OUT_OF_STOCK brand/denomination unavailable. Try another amount or brand. - PAYMENT_FAILED settlement failed. Surface to the operator. - INVOICE_EXPIRED the provider invoice expired. Start a new purchase. - PROVIDER_HOLD provider paused. Retry later; the kill switch may be active. - ALREADY_RELEASED get_code was already called for this task. The code was returned once only. - NOT_FOUND unknown task_id. - UNAUTHORIZED wrong agent or invalid key. - RETRYABLE transient. Wait `retry_after` seconds, then retry the same call. ## Guarantees you can rely on - Idempotent purchases: a repeated client_ref returns the existing task_id; a second charge is structurally impossible (unique payment per task). - Single-read codes: the code is encrypted at rest and released once; it is never logged or traced. - Fail-closed policy: if policy cannot be evaluated, the purchase is denied, never allowed. - Spend caps and human approval are enforced server-side; an agent cannot exceed its budget. ## Connecting from each platform All of these point at the same URL and token. Verified mid-2026; check each SDK's current MCP docs before shipping, since transport strings differ (LangChain uses `streamable_http`, CrewAI uses `streamable-http`). Claude (Code CLI): claude mcp add agentrefills --transport http \ https://mcp.agentrefills.com/mcp \ --header "Authorization: Bearer sk_live_..." # Claude Desktop: Settings -> Connectors -> Add custom connector -> paste URL -> OAuth sign-in OpenAI Responses API (hosted MCP tool): client.responses.create( model="gpt-5.1", tools=[{ "type": "mcp", "server_label": "agentrefills", "server_url": "https://mcp.agentrefills.com/mcp", "headers": {"Authorization": "Bearer sk_live_..."}, "require_approval": "never" }], input="Buy a $25 Amazon gift card") OpenAI Agents SDK (Python): from agents import Agent, Runner from agents.mcp import MCPServerStreamableHttp async with MCPServerStreamableHttp( name="agentrefills", params={"url": "https://mcp.agentrefills.com/mcp", "headers": {"Authorization": "Bearer sk_live_..."}}, ) as server: agent = Agent(name="Shopper", mcp_servers=[server]) result = await Runner.run(agent, "Buy a $25 Amazon gift card") LangChain / LangGraph: from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({"agentrefills": { "transport": "streamable_http", "url": "https://mcp.agentrefills.com/mcp", "headers": {"Authorization": "Bearer sk_live_..."}}}) tools = await client.get_tools() CrewAI: from crewai_tools import MCPServerAdapter server_params = {"url": "https://mcp.agentrefills.com/mcp", "transport": "streamable-http", "headers": {"Authorization": "Bearer sk_live_..."}} with MCPServerAdapter(server_params) as tools: Agent(role="Shopper", tools=tools) Vercel AI SDK (TypeScript): import { experimental_createMCPClient as createMCPClient } from "ai"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp"; const mcp = await createMCPClient({ transport: new StreamableHTTPClientTransport(new URL("https://mcp.agentrefills.com/mcp"), { requestInit: { headers: { Authorization: "Bearer sk_live_..." } } }) }); const tools = await mcp.tools(); Plain REST (no MCP): see the purchase flow above. ## Notes - Sandbox first: every call runs against test products until the account is switched to production. No real money moves in the sandbox. - Amounts are minor units: 2500 means $25.00. - Built on the Bitrefill network for fulfillment across 5,000+ brands.