SerenAI
SerenAI

Rust Cron Scheduler with x402 Payment Delegation on Cloudflare Workers

Taariq Lewis
Taariq Lewis
6 min read

A Rust-based scheduler compiled to WASM that lets AI agents run recurring jobs with automatic micropayments—no interactive signing required.

The Problem: Scheduled Jobs Can't Sign Payments

We hit a wall. Our AI agents needed to call pay-per-query APIs (like Alpaca Finance for stock data) on a schedule. But the x402 protocol requires cryptographic signatures for each payment—EIP-3009 transferWithAuthorization signatures that need a wallet's private key at execution time.

text
1Scheduler → API
23        402 Payment Required
45        ??? Who signs this at 3am?

Cron jobs run autonomously. There's no human to approve payments. There's no interactive wallet popup at 3am when your MSTR price monitor fires.

The naive solution—embedding the agent's private key in the scheduler—is a security nightmare. One compromised Worker means drained wallets.

We needed the scheduler to pay without holding keys.

Background: The x402 Protocol

x402 is an open HTTP payment protocol (championed by Coinbase) that uses the HTTP 402 status code. When an API requires payment, it returns HTTP 402 with payment requirements:

json
1{
2  "x402Version": 1,
3  "accepts": [{
4    "scheme": "exact",
5    "network": "base",
6    "maxAmountRequired": "300",
7    "payTo": "0x..."
8  }]
9}

The client must sign an EIP-3009 transferWithAuthorization—a gasless USDC transfer authorization—and retry with an X-PAYMENT header containing the signature. The server verifies, settles on-chain, and returns data.

This works great for interactive clients. For autonomous schedulers? Not so much.

The Solution: X-Payment-Delegation

We built payment delegation into our x402 gateway. When an automated service sends X-Payment-Delegation: true, the gateway handles the entire payment flow on behalf of the caller:

text
1┌─────────────────────────────────────────────────────────────────┐
2│                    PAYMENT DELEGATION FLOW                       │
3├─────────────────────────────────────────────────────────────────┤
4│                                                                  │
5│  1. Agent deposits USDC → Prepaid Credits (one-time)            │
6│                                                                  │
7│  2. Scheduler → Gateway (X-Payment-Delegation: true)            │
8│                    ↓                                             │
9│  3. Gateway → Upstream API                                       │
10│                    ↓                                             │
11│              402 Payment Required                                │
12│                    ↓                                             │
13│  4. Gateway signs EIP-3009 from gateway wallet                  │
14│     Gateway deducts from agent's prepaid credits                │
15│                    ↓                                             │
16│  5. Gateway → Upstream (with X-PAYMENT header)                  │
17│                    ↓                                             │
18│  6. Success! Response returned to Scheduler                     │
19│                                                                  │
20└─────────────────────────────────────────────────────────────────┘

The gateway acts as a payment facilitator. It holds USDC in its own wallet, signs payments from that wallet, and reconciles against each agent's prepaid credit balance. The scheduler never touches private keys. The agent never shares their keys with anyone.

Implementation

Gateway Side (TypeScript/Express)

The gateway detects the delegation header and handles upstream 402 responses:

typescript
1// Detect delegation mode
2const isDelegatedPayment = req.header('X-Payment-Delegation') === 'true';
3
4// Verify agent has prepaid credits before proceeding
5if (isDelegatedPayment) {
6  const hasBalance = await hasSufficientBalance(agentWallet, minimumBalance);
7  if (!hasBalance) {
8    return res.status(402).json({
9      error: 'Insufficient credit balance for delegated payment',
10      depositEndpoint: '/api/credits/confirm-deposit',
11    });
12  }
13}
14
15// When upstream returns 402...
16if (isDelegatedPayment && upstreamRequirements.length > 0) {
17  const requirement = upstreamRequirements[0];
18
19  // Sign EIP-3009 transferWithAuthorization from gateway wallet
20  const signedPayment = await createGatewaySignedPayment(requirement);
21
22  // Retry upstream with signed payment
23  const response = await forwardToUpstream(upstream, request, {
24    paymentHeader: signedPayment.paymentHeader,
25  });
26
27  // Deduct from agent's prepaid credits
28  await recordPrepaidUsage(agentWallet, publisherId, signedPayment.amountDecimal);
29
30  return res.json({ ...response.body, _x402: { delegatedPayment: true } });
31}

The createGatewaySignedPayment function generates EIP-712 typed data signatures for USDC's transferWithAuthorization. This is the same signature format used by Circle's USDC contract for gasless transfers:

typescript
1const signature = await wallet.signTypedData({
2  account: facilitatorAccount,
3  domain: buildEip712Domain(), // USDC contract on Base
4  types: TRANSFER_WITH_AUTH_TYPES,
5  primaryType: 'TransferWithAuthorization',
6  message: {
7    from: gatewayAddress,
8    to: requirement.payTo,
9    value: BigInt(requirement.maxAmountRequired),
10    validAfter: BigInt(now - 60),   // 1 min buffer for clock skew
11    validBefore: BigInt(now + 300), // 5 min validity window
12    nonce: toHex(crypto.getRandomValues(new Uint8Array(32)), { size: 32 }),
13  },
14});

Scheduler Side (Rust/Cloudflare Workers)

SerenCron is our Rust-based cron scheduler running on Cloudflare Workers. The entire payment integration is a single header:

rust
1let headers = Headers::new();
2headers.set("Content-Type", "application/json")?;
3headers.set("X-Payment-Delegation", "true")?;
4
5let request = serde_json::json!({
6    "publisherId": publisher_id,
7    "agentWallet": job.agent_wallet,
8    "request": {
9        "method": "GET",
10        "path": "/v2/stocks/MSTR/quotes/latest",
11        "query": {}
12    }
13});

The scheduler extracts cost metadata from gateway responses for job execution tracking:

rust
1fn extract_x402_cost(body: &str) -> Option<f64> {
2    let json: serde_json::Value = serde_json::from_str(body).ok()?;
3    // Delegated payments return decimal amounts in _x402.amountPaid
4    json.get("_x402")?.get("amountPaid")?.as_str()?.parse().ok()
5}

This cost gets stored in the execution result, giving agents visibility into exactly what each scheduled job costs.

Live Test: MSTR Quote via Alpaca

We tested with MicroStrategy stock (MSTR) via the Alpaca Finance publisher:

bash
1# Via x402 gateway with payment delegation
2$ curl -X POST https://x402.serendb.com/api/proxy \
3    -H "Content-Type: application/json" \
4    -H "X-Payment-Delegation: true" \
5    -d '{
6      "publisherId": "36454f6e-2b72-4341-8414-53a6da7da978",
7      "agentWallet": "0xYourWallet...",
8      "request": { "method": "GET", "path": "/v2/stocks/MSTR/quotes/latest" }
9    }'
10
11{
12  "quote": {
13    "ap": 160.00,
14    "bp": 155.00,
15    "t": "2025-12-26T17:21:10Z"
16  },
17  "symbol": "MSTR",
18  "_x402": {
19    "paymentSettled": true,
20    "delegatedPayment": true,
21    "gatewayPayer": "0x...",
22    "amountPaid": "0.0003",
23    "upstreamPayTo": "0x..."
24  }
25}

$0.0003 USDC paid automatically. No wallet popup. No human intervention. Settlement confirmed on Base mainnet in the same request cycle.

Security Model

Payment delegation is designed with defense in depth:

  • Gateway wallet holds USDC: Makes payments on behalf of agents, not from agent wallets
  • Agent prepaid balance checked first: Gateway verifies sufficient credits before making any payment—no overdrafts possible
  • Per-job budget limits: Jobs can set max_payment_per_call to cap exposure per execution
  • All payments logged: Every delegated payment records agent wallet, publisher, amount, timestamp, and upstream recipient
  • Gateway is reimbursed: Credits deducted from agent balance equal payments made by gateway

The agent never shares private keys. The gateway never accesses agent funds directly—only the prepaid credit balance the agent explicitly deposited via on-chain USDC transfer.

Use Cases

ServiceDescription
SerenCronScheduled jobs calling pay-per-query APIs on recurring schedules
AI AgentsAutonomous agents (Claude, GPT) making x402 API calls via MCP (Model Context Protocol)
CI/CD PipelinesBuild processes querying paid data sources for tests or deployments
Backend ServicesServer-side integrations with x402-enabled publishers

Any service that needs to make x402 payments without interactive signing can use delegation.

Build Your Own Publisher

SerenCron is itself a publisher on the x402 gateway. You can register your own API and start accepting USDC micropayments from AI agents.

1. Register via console: Sign up at console.serendb.com and create a publisher with your upstream API URL.

2. Configure pricing:

json
1{
2  "price_per_call": "0.0001",
3  "price_per_get": "0.0001",
4  "price_per_post": "0.0005",
5  "wallet_address": "0xYourWallet..."
6}

3. Receive payments:

When agents call your API through the gateway, USDC flows directly to your wallet. The gateway handles 402 responses, payment verification, and settlement—you just serve your API.

Publishers keep 100% of GET request payments. POST requests with publisher-set pricing include a 5% gateway fee. No subscription tiers, no usage caps, no invoicing.

Discovery for LLMs:

bash
1# Discover existing publishers
2curl https://x402.serendb.com/api/catalog
3
4# Learn publisher registration schema (returns validation errors showing all fields)
5curl -X POST https://x402.serendb.com/api/publishers/register \
6  -H "Content-Type: application/json" -d '{}'

AI agents can discover publishers via the catalog, or learn the registration schema by calling the register endpoint—Zod validation errors document all required and optional fields.

Acknowledgments

Special thanks to Cloudflare for the Workers credits that made SerenCron development possible. Running a Rust-based scheduler compiled to WASM on their edge network—with fast cold starts and global distribution—has been excellent for reliable cron job execution.

The x402 protocol from Coinbase provided the foundation. EIP-3009's transferWithAuthorization made gasless USDC payments practical.

About SerenAI

SerenAI is building payment infrastructure for the AI agent economy. Our x402 Gateway enables AI agents to autonomously pay for data and services using USDC micropayments on Base—no subscriptions, no API keys, no human intervention.

Our Stack: TypeScript, Rust, PostgreSQL, Cloudflare Workers, Base (Ethereum L2), x402 Protocol

Data Publishers: Alpaca, CoinGecko, Firecrawl, Perplexity, OpenAI, Moonshot AI, CoinMarketCap, and 20+ more

Ready to build? Sign up at console.serendb.com/signup with invite code serenai2025

Questions? Email hello@serendb.com or join our Discord.

Share:
Taariq Lewis

About Taariq Lewis

Exploring how to make developers faster and more productive with AI agents

Related Posts