Build a Machine-Payment Workflow with Human Spend Ceilings
Cloudflare Agents Week (Aug 4-15, 2026) launched Cloudflare Wallets and cloudflare.pay on the x402 protocol so agents can hold verifiable identity and purchase within human-set limits, plus an Identity-Aware AI Gateway (Aug 5, 2026) attaching verified identity to every outbound AI request. This dispatch builds machpay, a LangGraph payment workflow where an agent requests a purchase, the workflow validates the request against a human-set spend ceiling, checks wallet balance, settles via x402 in a single idempotent request, and logs every spend line to an audit ledger with identity attribution.
Deepak Bagada
CEO, SaaSNext
- x402 turns machine payment into a single signed HTTP request, so agents no longer borrow credit cards or embed shared secrets in prompts.
- A human-set spend ceiling per agent per cadence is enforced at the first gate — small-request routing cannot dodge the budget.
- Idempotency keys make settlement safe to retry: a duplicate request returns the prior settlement instead of charging twice.
- Every spend line lands in an append-only ledger with the x402 identity proof, so all spend reconciles to one verified agent identity.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Cloudflare's Agents Week (August 4–15, 2026) ended with something most agent platforms still lack: a payment rail built for machines. Cloudflare Wallets give agents verifiable identity and a store of funds, while cloudflare.pay — built on the x402 protocol — lets that identity make purchases within human-set limits in a single HTTP request. Two days into the week, on August 5, Cloudflare also launched its Identity-Aware AI Gateway, which attaches a verified identity to every outbound AI request, and brought MCP traffic detection into Cloudflare One. The thread connecting all of it is one idea: machine payments need the same controls that human payments got decades ago — identity, limits, and receipts.
This dispatch builds machpay, a LangGraph workflow that turns agent commerce into a governed flow. An agent requests a purchase; the workflow validates the request against a human-set spend ceiling; it checks the wallet balance; it settles via x402 in a single idempotent request; and it appends every spend line to an audit ledger with identity attribution. Same ceilings, same wallets, same receipts for every agent — the discipline composes with the rest of the AI workflows library.
Why machine payments needed a protocol
Agents were already paying for things, just badly — by borrowing a human's credit card, sharing API keys, or embedding credentials in a prompt. The x402 protocol removes the shared secret from the loop: a payment is a signed HTTP request from a wallet that holds a verifiable identity, and the merchant settles it in a single round trip. The security model is finally legible. The agent does not need your card number; it needs a wallet with a balance, a ceiling you set, and an identity the gateway can verify. That is the difference between "the AI bought something" and "the AI bought something within the budget, under a verified identity, with a receipt we can audit."
Architecture
flowchart TD
A[Agent submits PurchaseRequest] --> B[Validate request + load human spend ceiling]
B --> C{Within ceiling?}
C -- no --> D[Decline + ledger entry + notify human]
C -- yes --> E[Check wallet balance]
E --> F{Balance covers amount?}
F -- no --> D
F -- yes --> G[Settle via x402 single request]
G --> H{Idempotency key collision?}
H -- yes --> I[Return prior settlement - no double charge]
H -- no --> J[Stamp settled ledger entry with identity proof]
D --> K[Audit ledger]
I --> K
J --> K
Project setup
mkdir machpay && cd machpay
python -m venv .venv && source .venv/bin/activate
pip install langgraph langchain-openai pydantic httpx
# .env
OPENAI_API_KEY=sk-...
X402_ENDPOINT=https://pay.cloudflare.com/x402
WALLET_ID=w_agent_main
WALLET_TOKEN=wlt_...
SPEND_CEILING_USD=50.00
CEILING_CADENCE=weekly
AUDIT_LEDGER_PATH=./ledger/agent-spend.ndjson
IDP_ISSUER=https://idp.dailyaiworld.com
MAX_AMOUNT_PER_REQUEST_USD=25.00
schemas.py
from enum import Enum
from typing import Literal
from pydantic import BaseModel, Field
class CeilingCadence(str, Enum):
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
class PurchaseRequest(BaseModel):
request_id: str = Field(..., description="Idempotency key for x402")
agent_id: str = Field(..., description="Verified agent identity")
merchant: str
amount_usd: float = Field(..., gt=0)
reason: str
cadence: CeilingCadence = CeilingCadence.WEEKLY
class WalletState(BaseModel):
wallet_id: str
balance_usd: float
reserved_usd: float = 0.0
available_usd: float = Field(..., description="balance minus reserved")
class LedgerEntry(BaseModel):
request_id: str
agent_id: str
merchant: str
amount_usd: float
status: Literal["authorized", "settled", "declined", "pending"]
identity_proof: str = Field(..., description="x402 identity attestation")
ts: str
tools.py
import os, json, httpx
from datetime import datetime, timezone
from schemas import PurchaseRequest, WalletState, LedgerEntry
def load_ceiling(agent_id: str, cadence: str) -> float:
r = httpx.get(f"{os.getenv('IDP_ISSUER')}/ceiling",
params={"agent": agent_id, "cadence": cadence},
headers={"Authorization": f"Bearer {os.getenv('WALLET_TOKEN')}"},
timeout=10)
r.raise_for_status()
return float(r.json()["amount_usd"])
def spent_in_window(agent_id: str, cadence: str) -> float:
total = 0.0
with open(os.getenv("AUDIT_LEDGER_PATH"), encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
row = json.loads(line)
if row["agent_id"] == agent_id and row["status"] in ("authorized", "settled"):
total += row["amount_usd"]
return total
def check_balance(wallet_id: str) -> WalletState:
r = httpx.get(f"{os.getenv('X402_ENDPOINT')}/wallet/{wallet_id}",
headers={"Authorization": f"Bearer {os.getenv('WALLET_TOKEN')}"},
timeout=10)
r.raise_for_status()
return WalletState(**r.json())
def settle_x402(req: PurchaseRequest) -> dict:
r = httpx.post(f"{os.getenv('X402_ENDPOINT')}/purchase",
json=req.model_dump(),
headers={"Idempotency-Key": req.request_id,
"Authorization": f"Bearer {os.getenv('WALLET_TOKEN')}"},
timeout=20)
r.raise_for_status()
return r.json()
def stamp_ledger(entry: LedgerEntry):
with open(os.getenv("AUDIT_LEDGER_PATH"), "a", encoding="utf-8") as f:
f.write(f"{entry.model_dump_json()}
")
def alert_human(agent_id: str, amount_usd: float, reason: str):
# A declined or over-limit request always reaches a human. No payment
# is silently dropped.
print(json.dumps({"alert": "human_review", "agent": agent_id,
"amount_usd": amount_usd, "reason": reason}))
graph.py
from datetime import datetime, timezone
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END
from schemas import PurchaseRequest, WalletState, LedgerEntry
from tools import (load_ceiling, spent_in_window, check_balance,
settle_x402, stamp_ledger, alert_human)
class PayState(TypedDict):
request: PurchaseRequest
ceiling_usd: float
spent_usd: float
wallet: WalletState | None
settlement: dict | None
status: Literal["settled", "declined", "pending"]
def validate_node(state: PayState) -> PayState:
ceiling = load_ceiling(state["request"].agent_id,
state["request"].cadence.value)
spent = spent_in_window(state["request"].agent_id,
state["request"].cadence.value)
return {**state, "ceiling_usd": ceiling, "spent_usd": spent}
def route_ceiling(state: PayState) -> str:
if state["request"].amount_usd + state["spent_usd"] > state["ceiling_usd"]:
return "decline"
return "balance"
def balance_node(state: PayState) -> PayState:
wallet = check_balance(state["request"].agent_id)
return {**state, "wallet": wallet}
def route_balance(state: PayState) -> str:
if state["wallet"].available_usd < state["request"].amount_usd:
return "decline"
return "settle"
def settle_node(state: PayState) -> PayState:
result = settle_x402(state["request"])
stamp_ledger(LedgerEntry(
request_id=state["request"].request_id,
agent_id=state["request"].agent_id,
merchant=state["request"].merchant,
amount_usd=state["request"].amount_usd,
status="settled",
identity_proof=result["identity_proof"],
ts=result["ts"],
))
return {**state, "settlement": result, "status": "settled"}
def decline_node(state: PayState) -> PayState:
stamp_ledger(LedgerEntry(
request_id=state["request"].request_id,
agent_id=state["request"].agent_id,
merchant=state["request"].merchant,
amount_usd=state["request"].amount_usd,
status="declined",
identity_proof="",
ts=datetime.now(timezone.utc).isoformat(),
))
alert_human(state["request"].agent_id,
state["request"].amount_usd,
"over ceiling or insufficient balance")
return {**state, "status": "declined"}
def build_graph():
g = StateGraph(PayState)
g.add_node("validate", validate_node)
g.add_node("balance", balance_node)
g.add_node("settle", settle_node)
g.add_node("decline", decline_node)
g.set_entry_point("validate")
g.add_conditional_edges("validate", route_ceiling,
{"balance": "balance", "decline": "decline"})
g.add_conditional_edges("balance", route_balance,
{"settle": "settle", "decline": "decline"})
g.add_edge("settle", END)
g.add_edge("decline", END)
return g.compile()
main.py
import asyncio, json
from graph import build_graph
from schemas import PurchaseRequest, CeilingCadence
async def main():
graph = build_graph()
req = PurchaseRequest(
request_id="pay_7f3a91",
agent_id="agent-billing-01",
merchant="cloudflare-r2-storage",
amount_usd=4.20,
reason="store search index for Q3",
cadence=CeilingCadence.WEEKLY,
)
result = await graph.ainvoke({
"request": req, "ceiling_usd": 0.0, "spent_usd": 0.0,
"wallet": None, "settlement": None, "status": "",
})
print(json.dumps({
"status": result["status"],
"settlement_id": result["settlement"]["id"] if result["settlement"] else None,
}, indent=2))
if __name__ == "__main__":
asyncio.run(main())
How a purchase moves through the workflow
Every purchase starts as a PurchaseRequest with a request id that doubles as the x402 idempotency key. The validate node pulls the human-set spend ceiling for that agent and cadence, then sums the agent's already-settled and authorized spend from the ledger. The first conditional edge decides: if the new amount plus the spent-so-far exceeds the ceiling, the request declines and a human is alerted — the agent cannot spend its way around the budget by issuing smaller requests. If the ceiling holds, balance checks the wallet; a short wallet declines the same way.
Only a request that clears both gates reaches settle, which posts a single x402 request with the idempotency key. Because x402 settles in one round trip, there is no two-phase commit to babysit. The settled entry is stamped into the audit ledger with the identity proof returned by the wallet, so every dollar is attributable to a verified agent identity — the same identity your Identity-Aware AI Gateway attaches to every outbound AI request. Trace the whole commerce ecosystem in the MCP directory — payment MCP servers plug into this workflow as drop-in merchant adapters.
Retry rules
- The ceiling fetch retries twice with backoff; if the ceiling is unreachable, the request is denied by default — an agent never spends against an unknown budget.
- The wallet balance check retries once; a stale or failed balance check declines rather than guessing.
- x402 settlement retries once using the same idempotency key; a second failure marks the request
pendingand alerts a human. The idempotency key guarantees a retry can never double-charge. - Ledger writes retry three times; if the ledger is unwritable after settlement, the workflow still records a
pendingreconciliation entry and raises an alert — the money moved, so the record must exist. - The per-request amount cap (
MAX_AMOUNT_PER_REQUEST_USD) is enforced in validation and is never retried; a request over the cap declines immediately. - Identity verification failures deny the payment outright and log the failed attestation for review.
Ceilings, identity, and the audit ledger
The three controls are independent and all enforced. The ceiling is a human-set number per agent per cadence — a number no agent can change, because the agent never holds the wallet token. The wallet is where money actually sits, with a balance the workflow checks before any spend. The ledger is the receipt: every entry carries the request id, the agent identity, the merchant, the amount, and the identity proof from settlement. Because entries are append-only and the ledger is the single source of spend truth, spent_in_window and the human review both read the same book. Cloudflare's own Agents Week run-down made the same point — the latest AI news has the full recap — that the payment problem is not the protocol, it is the governance around it.
What this pattern adds to agent commerce
The workflow moves spend control from the protocol layer to a policy layer the business owns. x402 solves identity and settlement; machpay decides whether an agent may spend at all, up to what amount, and against which budget window. That separation matters because the protocol will keep improving while budgets are a matter of governance — every team adopting agent payments still needs a ceiling, a ledger, and an alert on decline. The same governed discipline is why payment adapters belong alongside this workflow in the MCP directory.
Testing the workflow
Feed the graph four scenarios. A request under the ceiling with a funded wallet settles, and the ledger gains a settled entry with an identity proof. A request that would cross the ceiling declines at the first gate and alerts a human, even if the wallet is funded. A short wallet declines at the second gate. And a retry with the same request id returns the prior settlement instead of charging again — that is the idempotency test, and it is the one that proves the workflow is safe to run unattended. Verify each case against the ledger and your wallet statement before you let an agent spend real money.
Frequently Asked Questions
What is x402?
A payment protocol for machine payments: a signed HTTP request from a wallet with a verifiable identity, settled by the merchant in a single round trip. It removes shared secrets like credit card numbers from the agent loop entirely.
How are spend ceilings enforced?
A human sets a ceiling per agent per cadence. The workflow sums already-settled spend from the audit ledger, and any request that would push past the ceiling declines at the first gate — the agent cannot route around the budget with smaller requests.
What stops a double charge on retry?
Every request carries an idempotency key that is reused across retries. If the same key reaches x402 again, the provider returns the prior settlement instead of charging a second time.
What if the wallet balance is short?
The request declines at the balance gate and a human is alerted. The agent is told the outcome; it never sees wallet internals or the wallet token.
How is identity attributed to a spend?
Each ledger entry records the verified agent identity plus the identity proof returned by the wallet at settlement. The same identity attaches to outbound AI requests via the Identity-Aware AI Gateway, so spend and actions reconcile to one identity.
Closing thoughts
Cloudflare's Agents Week gave machines an identity, a wallet, and a payment protocol — but a protocol does not enforce a budget. machpay supplies the governance layer: a human-set ceiling that no agent can change, a wallet check before any spend, an idempotent single-request settlement, and an append-only ledger with identity attribution. Run the workflow, keep the ledger honest, and let the agents shop within the lines you drew. The full pattern library lives at AI workflows.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Hazmat: Sandboxing AI Coding Agents with Least Privilege
Next Story →Meta Muse Glimmer: The Open 30B Agentic Model for Your Device
Related Intelligence Analysis
The Step-by-Step Guide to Automating Meeting Tasks with Whisper
You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...
Lovable AI UI-to-Code Pipeline: 2026 Tutorial
Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...
Claude Code's New Browser: 5 Workflows That Save Hours Daily
Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...