---
title: "Create an email-native payment challenge"
method: POST
path: "/x402/email-challenges"
tags: ["Payments"]
---

# Create an email-native payment challenge

`POST /x402/email-challenges`

Issue an x402 payment challenge over a real email thread (the payee
side). Unlike `createChallenge` (which mints a synthetic challenge id),
this sends the challenge as an email from `from` to `to` and binds the
payment to that DKIM-authenticated thread. The `pay_to` address and the
token asset are resolved server-side from your registered default payout
address for the network, never from the request. The response carries
the thread's `interaction_id` plus the `challenge` (the
`payment_requirements`, the `nonce_binding`, and `expires_at`) the payer
needs to sign; the payer replies with a signed `payment` interaction
step. Amounts are in token base units (USDC has 6 decimals, so `"10000"`
is 0.01 USDC).

## Headers

- `Idempotency-Key` string

## Request body

- object — Issue a payment challenge over an email thread. `from` is your sending address (the funds receiver; ownership is enforced at send, exactly as for outbound mail) and `to` is the payer's address. The `pay_to` payout wallet and the token asset are resolved server-side, never taken from the request.
  - `from` string, email, required — Your sending address (the payee / funds receiver). Must be an address your org is allowed to send from.
  - `to` string, email, required — The payer's email address the challenge is sent to.
  - `amount` string, required — Amount to collect, in token base units (unlike the `charge` CLI command, which also accepts `--amount-usdc`, this field takes base units only). USDC has 6 decimals, so `"10000"` is 0.01 USDC: multiply a human USDC amount by 1,000,000 (0.01 USDC -> `"10000"`).
  - `network` 'base' | 'base-sepolia', required
  - `expires_in` integer — Seconds until the challenge expires. Defaults to 300.
  - `resource` string, uri — Optional URL identifying what is being paid for.
  - `description` string — Optional human-readable description of the payment.

## Response `200`

Idempotent replay: a request with a previously-used idempotency key
returns the original issued challenge without sending a second email.

- object
  - `success` true, required
  - `data` object, required — The result of issuing an email-native payment challenge. `interaction_id` is the real email thread id (`uuid@domain`) the payment is bound to; `challenge_id` is the underlying challenge record. Hand the `challenge` to the payer, who replies with a signed `payment` interaction step (the SDK `payEmailChallenge` helper builds it).
    - `interaction_id` string, required — The email thread id (`uuid@domain`) the payment is bound to.
    - `challenge_id` string, uuid, required — The underlying challenge record id.
    - `challenge` object, required — The challenge the payer needs to sign and pay, carried inside an email-native challenge response.
      - `payment_requirements` object, required — The x402 `PaymentRequirements` the payer signs over. Field names are x402's native camelCase, preserved byte-for-byte.
        - `scheme` string, required — The x402 settlement scheme. Always `exact` for v1.
        - `network` 'base' | 'base-sepolia', required
        - `maxAmountRequired` string, required — Amount in token base units.
        - `payTo` string, required — The payee's resolved payout address (checksummed).
        - `asset` string, required — The token contract address (checksummed). USDC.
        - `resource` string
        - `description` string
        - `maxTimeoutSeconds` integer
        - `extra` object, required — The token's load-bearing EIP-712 domain params. `name` differs by chain (Base mainnet USDC is `USD Coin`, Base Sepolia is `USDC`); a wrong value produces a signature the verifier rejects.
          - `name` string, required
          - `version` string, required
      - `nonce_binding` object, required — The interaction binding the payer hashes into the EIP-3009 nonce (`deriveEip3009Nonce`). Pinning the nonce to this binding is what lets an x402 payment ride asynchronous transports safely: a replayed challenge can't redirect funds and a signed payment can't settle twice.
        - `interaction_id` string, required — Interaction id, including its `@domain` part.
        - `challenge_step_id` string, uuid, required
        - `challenge_nonce` string, required — 32 random bytes as 64 lowercase hex chars.
      - `expires_at` string, date-time, required — ISO-8601 expiry of the challenge.

## Other responses

- `201` — Email challenge issued
- `400` — Invalid request parameters
- `401` — Invalid or missing API key
- `403` — Authenticated caller lacks permission for the operation
- `409` — The request conflicts with the current state of the resource
- `422` — The request was well-formed but could not be processed. For Payments this covers a missing payout address, a failed payment verification, a spend-policy decline, or an expired challenge; `error.code` distinguishes them.
- `429` — Rate limit exceeded

---

[API](https://skmtc.dev/primitive/apis/primitive-api.md) · [All operations](https://skmtc.dev/primitive/apis/primitive-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/primitive/primitive-api/revisions/c2299d28022c/schema)
