Skip to main content
With Turnkey, you can give AI agents and automated systems the ability to send, receive, and track onchain payments, free from gas friction or fragmented infrastructure. Turnkey offers non-custodial wallets, transaction lifecycle management, gas sponsorship, and real-time balances through a single integration. For agent wallet provisioning and policy setup, start with Agentic Wallets. This guide covers the key decisions for building autonomous payment flows, then walks through a production example end-to-end: creating agent wallets, defining payment controls, executing sponsored transactions, and monitoring balances.

Powered by Turnkey

  • Catena built agentic treasury and banking infrastructure on Turnkey, giving AI agents their own accounts, payments, and treasury management. Policy guardrails are enforced at the enclave level, including tamper-proof spend limits, multi-party approvals, and agent-to-agent commerce with built-in identity verification.

Key implementation decisions

DecisionWhat to consider
Wallet architectureDedicate wallets per agent or per payment flow. Agents should never share wallets across different trust levels. See Agentic Wallets for provisioning patterns.
Payment controlsDefine policies that scope what agents can pay, where funds can move, and under what conditions. Use spending caps and destination allowlists to limit blast radius.
Transaction managementUse Turnkey’s transaction lifecycle management so agents can sign and broadcast in a single API call, with built-in confirmation tracking and retries.
Transaction monitoringUse webhooks to track transaction status, confirmations, and failures so agents can react to payment outcomes without polling.
Gas strategySponsored transactions eliminate the need for agents or end users to hold native tokens for gas.
Balance visibilityAgents need to know what they can spend before acting. Use real-time balances to inform payment decisions without manual reconciliation.
Human-in-the-loopFor high-value payments, require multi-party approval (agent + human, or agent + agent) before the enclave signs.
Chain coverageEVM and SVM have different transaction models and require separate policies. Plan for the networks your agents will transact on.

Example: autonomous payment agent

The following examples walk through three agentic payment patterns, all using the same agent wallet and policy foundation:
  1. Stablecoin payout - Agent sends USDC to an approved recipient, covering wallet setup, policies, gas sponsorship, and balance checks.
  2. x402 payment - Agent responds to an HTTP 402 Payment Required response by completing an onchain payment automatically.
  3. MPP payment - Agent fulfills a payment request from a service using Stripe’s Machine Payments Protocol.

1. Stablecoin payout

This example assumes you’ve provisioned an agent wallet and credentials following the Agentic Wallets guide. The agent client below uses those credentials.
1

Initialize the agent client

Create a Turnkey client using the agent’s API credentials:
import { Turnkey } from "@turnkey/sdk-server";

const agentClient = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
  defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID!,
}).apiClient();
2

Check available balance

Before initiating a payment, the agent checks that it has sufficient funds:
const { balances } = await agentClient.getWalletAddressBalances({
  address: process.env.AGENT_WALLET_ADDRESS!,
  caip2: "eip155:8453", // Base mainnet
});

const usdc = balances.find((b) => b.symbol === "USDC");
console.log("Available USDC:", usdc?.display?.crypto);
3

Define a payment policy

Restrict the agent to only interact with the USDC contract. This policy is created once by an admin:
{
  "policyName": "Restrict agent to USDC on Base",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(user, user.id == '<AGENT_USER_ID>')",
  "condition": "activity.type == 'ACTIVITY_TYPE_ETH_SEND_TRANSACTION' && wallet.id == '<AGENT_WALLET_ID>' && eth.tx.to == '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'"
}
The policy above restricts which contract the agent can interact with. To control the actual recipient addresses within an ERC-20 transfer, you can upload the contract ABI and write policies against individual function parameters. See Smart Contract Interfaces to learn more.
4

Send the USDC payout with gas sponsorship

With gas sponsorship enabled, the agent doesn’t need to hold ETH to pay for transaction fees. Turnkey covers gas so the agent can transact using only its USDC balance:
const sendTransactionStatusId = await agentClient.ethSendErc20Transfer({
  transfer: {
    from: process.env.AGENT_WALLET_ADDRESS!,
    to: "0xRecipientAddress",
    tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
    amount: "1000000", // 1 USDC (6 decimals)
    caip2: "eip155:8453",
    sponsor: true,
  },
});
5

Track the transaction

Once the transaction is broadcast, the agent polls for status updates to confirm the payment landed onchain:
const result = await agentClient.pollTransactionStatus({
  sendTransactionStatusId,
});

console.log("Transaction hash:", result.eth?.txHash);
console.log("Status:", result.status); // INCLUDED, FAILED, etc.

2. x402 payment

The x402 protocol revives HTTP’s 402 Payment Required status code as a payment rail for autonomous agents. When an agent requests a paid resource, the server responds with a 402 and payment requirements. The agent completes the onchain payment and retries the request. Turnkey plugs into this flow through @turnkey/viem, which provides a viem-compatible account that signs via the secure enclave. This replaces the raw private key that x402 clients normally use.
1

Create a Turnkey-backed viem account

Instead of using a raw private key with privateKeyToAccount(), create a viem account backed by Turnkey:
import { createAccount } from "@turnkey/viem";

const turnkeyAccount = await createAccount({
  client: agentClient,
  organizationId: process.env.TURNKEY_ORGANIZATION_ID!,
  signWith: process.env.AGENT_WALLET_ADDRESS!,
});
2

Register the x402 client

Pass the Turnkey account to the x402 EVM scheme. The ExactEvmScheme accepts any viem account, so the Turnkey signer works as a drop-in replacement:
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";

const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(turnkeyAccount));

const fetchWithPayment = wrapFetchWithPayment(fetch, client);
3

Make a paid request

The wrapped fetch handles the full 402 flow automatically: it makes the initial request, parses the payment requirements, signs the payment via Turnkey, and retries with the payment header:
const response = await fetchWithPayment("https://api.example.com/data", {
  method: "GET",
});

const data = await response.json();

3. MPP payment

The Machine Payments Protocol (MPP) is an open standard co-authored by Tempo and Stripe that lets agents pay for services over HTTP. Like x402, an MPP-enabled server responds with a 402 challenge when payment is required. The agent signs a payment credential and retries the request. The Turnkey integration follows the same pattern as x402: the mppx client accepts a viem account, so a Turnkey-backed signer works as a drop-in.
1

Create the MPP client with a Turnkey signer

Pass the Turnkey-backed viem account from the previous example to the MPP client. The tempo method handles stablecoin payments on the Tempo network:
import { Mppx, tempo } from "mppx/client";

const mppx = Mppx.create({
  methods: [tempo({ account: turnkeyAccount })],
  polyfill: false,
});
2

Make a paid request

The MPP client handles the full 402 flow automatically: it intercepts the challenge, signs a payment credential via Turnkey, and retries the request:
const response = await mppx.fetch("https://api.example.com/paid-resource", {
  method: "GET",
});

const data = await response.json();

Next steps