Documentation Index: Fetch llms.txt first to discover every published page. This page is also available as Markdown at /cli-overview/cli-payments.md.
Verified · 8/11/2026

x402 Payments from the CLI

Register a payout address, create and pay x402 USDC challenges (including the email-native flow), and inspect settlements and spend policy directly from the terminal with the primitive payments command group.

The primitive payments command group drives the same non-custodial x402 payment model as the SDKs, from your shell. Reach for it when you want to register a payout address, request or settle a USDC payment, or inspect settlements and spend policy without writing code, for example scripting a payment into CI, or testing a payment flow before wiring it into a Function.

One agent registers a payout address and requests a payment with charge; the paying agent signs locally with its own wallet key and settles with pay. The signing key never leaves your machine, it is read from an environment variable or flag and used only to sign locally. Networks are base (mainnet) and base-sepolia (testnet); amounts take either a human USDC value (--amount-usdc 0.01) or token base units (--amount 10000, since USDC has 6 decimals). Your org is resolved automatically from your API key, so payout registration takes no org flag.

Note

This page assumes you already have the CLI installed and authenticated. See What is the Primitive CLI? and Authentication: login, signup, logout, whoami if you haven't set that up yet.

Set your wallet key#

The signing commands (register-payout-address, pay, pay-email, pay-email-step) need a wallet private key to sign locally. Set it as an environment variable so it never lands in shell history or the process list:

export PRIMITIVE_X402_PRIVATE_KEY=0x...

A --private-key flag exists as an escape hatch for scripted use, but the environment variable is preferred.

The non-signing commands (charge, get-challenge, list-payout-addresses, get-spend-policy, update-spend-policy) need only your Primitive API key (set via primitive login or PRIMITIVE_API_KEY); they don't touch the wallet key at all.

Register a payout address (payee, one time)#

Do this once per org before you ever call charge. It signs an ownership message locally with your wallet key and proves control of the address; the recovered address becomes your org's default payout destination for that network.

  1. 1

    Export your wallet key#

    export PRIMITIVE_X402_PRIVATE_KEY=0x...
    
  2. 2

    Register the address#

    primitive payments register-payout-address --network base-sepolia --label treasury
    

    The command prints a human-readable summary by default. Pass --json for the raw registration object.

Tip

Your org id is resolved automatically from your API key, payout registration takes no --org flag.

Create a payment challenge (payee)#

charge creates an x402 payment challenge with a human USDC amount and prints the challenge JSON to stdout, with a one-line summary on stderr:

primitive payments charge --network base-sepolia --amount-usdc 0.01

Capture the challenge JSON to hand to the payer:

primitive payments charge --network base-sepolia --amount-usdc 0.01 > challenge.json

charge is the friendly verb that matches the SDKs' charge() / Charge() and accepts --amount-usdc. The lower-level create-challenge command takes base-unit --amount instead; either creates a challenge.

Pay a challenge (payer)#

pay signs and settles the challenge locally, using your wallet key. It reads the challenge inline, from a file, or piped on stdin:

primitive payments pay --challenge-file challenge.json
cat challenge.json | primitive payments pay

pay prints a human-readable summary by default, and raw JSON with --json.

Warning

pay needs PRIMITIVE_X402_PRIVATE_KEY (or --private-key) set to your wallet's private key. Anyone with that key can sign payments from your address, keep it out of shell history and CI logs.

Inspect a challenge or your payout addresses#

Look up a challenge by id, or list every payout address registered for your org:

primitive payments get-challenge --id <challenge-id>
primitive payments list-payout-addresses

Email-native payments#

The email-native x402 payment flow rides a real email thread instead of an out-of-band challenge id. The payee issues the challenge as an email; the payer signs it into an interaction.json payment step and sends it back.

  1. 1

    Payee: issue the challenge over email#

    create-email-challenge takes --amount in token base units only, unlike charge it has no --amount-usdc. USDC has 6 decimals, so multiply by 1,000,000: 0.01 USDC is --amount 10000.

    primitive payments create-email-challenge --from payee@your-domain.example \
      --to payer@their-domain.example --amount 10000 --network base-sepolia
    
  2. 2

    Payer: sign and send in one step#

    pay-email is the recommended payer path: it signs the challenge locally with your wallet key AND sends the signed interaction.json attachment, so you skip the manual sign-then-send dance. --in-reply-to is the inbound challenge email you received; it's fetched to address the payment to the payee, with From defaulting to the address the challenge was sent to. The send is not threaded under the challenge itself, the payment associates by interaction_id instead.

    primitive payments pay-email --challenge-file challenge.json \
      --in-reply-to <inbound-challenge-email-id> --wait
    

    The message carries a short default note alongside the attachment; pass --body to customize it.

For advanced cases where you need to deliver the signed envelope yourself (for example attaching it with primitive send --attachment), sign only without sending:

primitive payments pay-email-step --challenge-file challenge.json > interaction.json
Tip

All four signing commands (register-payout-address, pay, pay-email, pay-email-step) accept --json. pay-email and pay-email-step print JSON by default (the send result and the interaction.json bytes, respectively); --json switches them to a fuller envelope object.

Read and update the spend policy#

The spend policy guards outbound payments with a kill-switch, per-payment and daily caps in token base units, and a payee allowlist. Read the current policy, or update it, the update merges, so omitted fields keep their current value:

primitive payments get-spend-policy
primitive payments update-spend-policy --max-per-payment 5000000

For the full flag list of any command, run:

primitive payments <command> --help

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer