Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clinerules/nevermined-payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor })

## Autonomous Operations (REST, no SDK)

When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `billingModel`, `creditsRedeemed`, `remainingBalance`, `orderTx`; on `pay-as-you-go` the two credit fields are always `"0"` even on a successful charge, so the proof is `success` + a non-empty `orderTx`/`transaction`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.

## Full Skill Reference

Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/nevermined-payments.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor })

## Autonomous Operations (REST, no SDK)

When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — minting the first API key, plus card enrollment if paying by card (the stablecoin path needs neither afterward). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `billingModel`, `creditsRedeemed`, `remainingBalance`, `orderTx`; on `pay-as-you-go` the two credit fields are always `"0"` even on a successful charge, so the proof is `success` + a non-empty `orderTx`/`transaction`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — minting the first API key, plus card enrollment if paying by card (the stablecoin path needs neither afterward). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
- Pin the API version on direct REST calls: `Nevermined-Version: <MAJOR.MINOR>` (discover via `GET /api/v1/meta/versions`; SDKs send it automatically). Never silently change an API key's stored version pin.
- Delegation first: create a spending delegation (`POST /delegation/create` with `provider` + `currency` + `spendingLimitCents` + `durationSecs`, or SDK `createDelegation`) and reuse its `delegationId`. The inline create-on-the-fly `delegationConfig` (limits without a `delegationId`) is **deprecated since `@nevermined-io/payments` 1.8.0** — it only emits a runtime warning; always pass an explicit `delegationId`.
- Seller analytics: discover your `orgId` from `.orgId` on `GET /protocol/plans` / `/protocol/agents` records. A malformed/placeholder `orgId` returns a silent 200-of-zeros; non-Premium → `403 BCK.ORGANIZATIONS.0022`; non-admin → `403 BCK.AUTH.0004`.
Expand Down
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ When an agent must act on its own behalf at runtime (buy a plan, enroll a card,
# 1. token
POST /api/v1/x402/permissions { "accepted": { "scheme": "nvm:erc4337", "network": "eip155:84532", "planId": "<id>" }, "delegationConfig": { "delegationId": "<id>" } } → { accessToken }
# 2. settle (proof of purchase)
POST /api/v1/x402/settle { "paymentRequired": { "x402Version": 2, "resource": { "url": "<url>" }, "accepts": [ { "scheme": "nvm:erc4337", "network": "eip155:84532", "planId": "<id>", "extra": {} } ], "extensions": {} }, "x402AccessToken": "<token>" } → { creditsRedeemed, remainingBalance }
POST /api/v1/x402/settle { "paymentRequired": { "x402Version": 2, "resource": { "url": "<url>" }, "accepts": [ { "scheme": "nvm:erc4337", "network": "eip155:84532", "planId": "<id>", "extra": {} } ], "extensions": {} }, "x402AccessToken": "<token>" } → { billingModel, creditsRedeemed, remainingBalance, orderTx } # billingModel is "credits" | "pay-as-you-go"; on pay-as-you-go both credit fields are always "0" even on a successful charge — the proof is success + a non-empty orderTx/transaction
```

Card payments: `scheme: "nvm:card-delegation"`, `network: "stripe"`. A human is needed only for one-time setup — minting the first API key, plus card enrollment if paying by card (the stablecoin path needs neither afterward). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
Expand Down
2 changes: 1 addition & 1 deletion .windsurf/rules/nevermined-payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor })

## Autonomous Operations (REST, no SDK)

When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `billingModel`, `creditsRedeemed`, `remainingBalance`, `orderTx`; on `pay-as-you-go` the two credit fields are always `"0"` even on a successful charge, so the proof is `success` + a non-empty `orderTx`/`transaction`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.

## Full Skill Reference

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor })

## Autonomous Operations (REST, no SDK)

When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.
When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `billingModel`, `creditsRedeemed`, `remainingBalance`, `orderTx`; on `pay-as-you-go` the two credit fields are always `"0"` even on a successful charge, so the proof is `success` + a non-empty `orderTx`/`transaction`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`.

## Nevermined Router — paying external services
{/* Keep this Router section in sync with the identical one in .github/copilot-instructions.md (and vice-versa). MDX comment syntax is deliberate — Mintlify parses this file and rejects HTML comments. */}
Expand Down
2 changes: 1 addition & 1 deletion agents-guide/check-credits.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Check Your Credits (Buyer)"
description: "Have an AI agent report your Nevermined buyer status — credits remaining on each plan, active delegations, and remaining spending budgets — so it can top up before running out."

Check warning on line 3 in agents-guide/check-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

agents-guide/check-credits.mdx#L3

Did you really mean 'Nevermined'?
icon: "wallet"
---

Expand Down Expand Up @@ -83,7 +83,7 @@
</Tabs>

<Note>
The receipt from a purchase (`creditsRedeemed` / `remainingBalance`, returned by `settle` or decoded from the `payment-response` header) is also a live proof of your balance right after a buy.
The receipt from a purchase (`creditsRedeemed` / `remainingBalance`, returned by `settle` or decoded from the `payment-response` header) is also a live proof of your balance right after a buy — **on a `credits` plan**. A pay-as-you-go plan holds no credit balance: its receipt reads `"0"` for both fields whatever was charged, and the charge is referenced by `orderTx` (fiat rails) or `transaction` (crypto rails). Read `billingModel` off the receipt before either field.
</Note>
</Step>
</Steps>
Expand All @@ -98,6 +98,6 @@
Grant a fresh budget when a delegation is spent or expired.
</Card>
<Card title="Order Plans" icon="receipt" href="/getting-started/ai-agent-purchase">
Buy credits upfront for stablecoin plans.

Check warning on line 101 in agents-guide/check-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

agents-guide/check-credits.mdx#L101

Did you really mean 'stablecoin'?
</Card>
</CardGroup>
48 changes: 39 additions & 9 deletions api-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3032,21 +3032,51 @@
},
"SettlePermissionResponse": {
"type": "object",
"description": "Settlement receipt. `success` alone tells you the settle worked; what to check in addition depends on `billingModel`. On `credits` plans the payment shows up as `creditsRedeemed > 0`. On `pay-as-you-go` plans there is no credit balance, so `creditsRedeemed` and `remainingBalance` are always the string \"0\" even on a charge that succeeded — the proof there is `success: true` plus a non-empty `orderTx` (fiat rails) or `transaction` (crypto rails). Do not gate on `creditsRedeemed` without reading `billingModel` first: on a card rail that reports a real charge as a decline and invites a retry of a payment that already went through. Note the credit fields are strings, so \"0\" is truthy while Number(\"0\") > 0 is false.",
"properties": {
"success": {
"type": "boolean"
"type": "boolean",
"description": "Whether settlement was successful."
},
"permissionHash": {
"type": "string"
"errorReason": {
"type": "string",
"description": "Reason for settlement failure (only present if success is false)."
},
"creditsSettled": {
"type": "integer"
"payer": {
"type": "string",
"description": "Address of the payer's wallet."
},
"remainingCredits": {
"type": "integer"
"transaction": {
"type": "string",
"description": "Blockchain transaction hash. On crypto pay-as-you-go plans this is also the reference for the per-request charge."
},
"txHash": {
"type": "string"
"network": {
"type": "string",
"description": "Network identifier. The discriminator is the rail, not the billing model: a CAIP-2 chain id (eip155:84532) for crypto rails under both billing models, or the settling payment provider (stripe, braintree, visa) for fiat card-delegation rails.",
"example": "eip155:84532"
},
"billingModel": {
"type": "string",
"enum": [
"credits",
"pay-as-you-go"
],
"description": "Which billing model this settle was priced under. Present regardless of `success` — check `success` before treating it as evidence of a charge. Read it before the two credit fields."
},
"creditsRedeemed": {
"type": "string",
"description": "Number of credits redeemed. Always \"0\" for pay-as-you-go plans, which hold no credit balance — including on a settle that charged the buyer successfully.",
"example": "2"
},
"remainingBalance": {
"type": "string",
"description": "Subscriber's remaining credit balance. Always \"0\" for pay-as-you-go plans — the per-request charge is referenced by `orderTx` (fiat) or `transaction` (crypto), not here.",
"example": "98"
},
"orderTx": {
"type": "string",
"description": "Reference for the order or per-request charge, if one occurred. On fiat pay-as-you-go this is the per-request charge (a Stripe PaymentIntent id, a Braintree transaction id); crypto pay-as-you-go reports its on-chain order in `transaction` instead. On credits plans it is set only when the settle had to order credits first (auto top-up).",
"example": "pi_3TUrvfBYvSRKcV420xCBjHb1"
}
}
},
Expand Down
18 changes: 14 additions & 4 deletions development-guide/nevermined-x402.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Nevermined x402"

Check warning on line 2 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L2

Did you really mean 'Nevermined'?
description: "Nevermined provides first-class support for the x402 payment protocol, enabling AI agents, APIs, and services to charge per-request using secure, locally-signed payment authorizations."

Check warning on line 3 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L3

Did you really mean 'Nevermined'?
icon: "credit-card"
related:
- Get a Nevermined API key: /docs/agents-guide/get-api-key
Expand All @@ -8,21 +8,21 @@
- Register a plan and agent: /docs/agents-guide/register-plan-and-agent
---

# Nevermined x402

Check warning on line 11 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L11

Did you really mean 'Nevermined'?

Nevermined provides first-class support for the x402 payment protocol, enabling AI agents, APIs, and services to charge per-request using secure, locally-signed payment authorizations.

Check warning on line 13 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L13

Did you really mean 'Nevermined'?

<Note>
For the complete technical specification, see the [x402 Smart Accounts Extension Spec](/specs/x402-smart-accounts).
</Note>

<Note>
**x402 has a sibling protocol: MPP.** Nevermined also speaks the **Merchant Payment Protocol (MPP)**, which settles against the **same Payment Plans, credits, and delegation** as x402 — a request that costs 2 credits burns 2 either way. The only difference is the wire handshake. A service tells you which it speaks by its `402` response:

Check warning on line 20 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L20

Did you really mean 'Nevermined'?

- an x402 `accepts` / `payment-required` body → follow the x402 steps on this page;
- a `WWW-Authenticate: Payment …` header → follow [Paying an MPP-protected service](#paying-an-mpp-protected-service) below.

MPP here is the **plan-model sibling of x402**, offered the same way — not the buyer-side [Router MPP rail](/products/catalog/router/rails-mpp), which pays *external* merchants that were never onboarded to Nevermined.

Check warning on line 25 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L25

Did you really mean 'onboarded'?

Check warning on line 25 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L25

Did you really mean 'Nevermined'?
</Note>

## Overview
Expand All @@ -30,7 +30,7 @@
This section explains:

- The x402 HTTP 402 handshake and `PAYMENT-SIGNATURE` retry pattern
- How Nevermined extends x402 with Smart Account-based settlement

Check warning on line 33 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L33

Did you really mean 'Nevermined'?
- How subscribers generate and sign x402 payment proofs
- How delegations, session keys, and delegated execution work
- How the facilitator verifies and settles requests
Expand All @@ -41,7 +41,7 @@

- [Payment Models](/integrate/patterns/payment-models)

Nevermined's x402 implementation is compatible with the standard protocol while adding programmable settlement layers powered by Nevermined smart contracts.

Check warning on line 44 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L44

Did you really mean 'Nevermined's'?

Check warning on line 44 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L44

Did you really mean 'Nevermined'?

## Background: What x402 Solves

Expand All @@ -56,21 +56,21 @@
- Checks balance/permissions
- Settles payment on-chain (EIP-3009 or equivalent)

Nevermined extends this with ERC-4337 Smart Accounts, session keys, and programmable billing models, allowing much more complex behavior than standard EIP-3009-based transfers.

Check warning on line 59 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L59

Did you really mean 'Nevermined'?

## Why Nevermined Extends x402

Check warning on line 61 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L61

Did you really mean 'Nevermined'?

x402 itself focuses on single ERC-20, pay-per-request flows.

Nevermined introduces:

Check warning on line 65 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L65

Did you really mean 'Nevermined'?

| Area | x402 | Nevermined |

Check warning on line 67 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L67

Did you really mean 'Nevermined'?
| --------------------- | --------------- | ------------------------------------------------------------------- |
| Payment authorization | EIP-3009 | ERC-4337 UserOps + session keys |
| Wallet model | EOA signatures | Smart Accounts with granular permissions |
| Billing models | Pay-per-request | Subscriptions, credits, time windows, credit-burning |
| Delegated execution | Basic | Fully programmable “burn”, “order”, “redeem”, plan-specific actions |
| Settlement layer | ERC-20 | Nevermined smart-contract settlement |

Check warning on line 73 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L73

Did you really mean 'Nevermined'?


This means a subscriber can grant a server the ability to redeem credits or burn usage units while maintaining strict control over what the server can do.
Expand All @@ -92,17 +92,17 @@

- Subscriber: owns a Smart Account; delegates permissions through smart account policies
- Server/Agent: exposes an API secured by x402
- Facilitator: Nevermined component that verifies and settles payments

Check warning on line 95 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L95

Did you really mean 'Nevermined'?
- Blockchain: executes credit burns, orders, or other plan-specific actions

Check warning on line 96 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L96

Did you really mean 'Blockchain'?


## The Nevermined x402 Extensions

Check warning on line 99 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L99

Did you really mean 'Nevermined'?

Nevermined introduces two x402 schemes for different payment rails:

Check warning on line 101 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L101

Did you really mean 'Nevermined'?

| Scheme | Network | Use Case | Settlement |
|--------|---------|----------|------------|
| `nvm:erc4337` | `eip155:84532` | Crypto payments | ERC-4337 UserOps + session keys |

Check warning on line 105 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L105

Did you really mean 'Crypto'?
| `nvm:card-delegation` | `stripe` | Fiat/credit card | Stripe PaymentIntent + credit burn |

For the complete delegation specification, see the [Delegation Spec](/specs/x402-card-delegation).
Expand All @@ -128,7 +128,7 @@
**`network` is the settlement *rail*, not the environment.** Send `eip155:<chainId>` (e.g. `eip155:84532`) for a crypto/ERC-4337 plan, or one of `stripe` / `braintree` / `visa` for a card-delegation plan. It is **not** the environment name — do **not** put `sandbox` or `live` here (that value fails validation). The environment is selected by your API key and base URL, never by this field.
</Note>

**Crypto plan (`nvm:erc4337`):**

Check warning on line 131 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L131

Did you really mean 'Crypto'?

```json
{
Expand Down Expand Up @@ -221,7 +221,7 @@

## Complete Payment & Execution Flow

Below is the Nevermined x402 Smart Account flow (verification + settlement).

Check warning on line 224 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L224

Did you really mean 'Nevermined'?

```mermaid
sequenceDiagram
Expand Down Expand Up @@ -284,7 +284,7 @@
- Execute order (if needed) to top up credits
- Execute burn to deduct usage
- Submit UserOps on-chain
- Return tx hashes to the server

Check warning on line 287 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L287

Did you really mean 'tx'?

## Developer Guide: Subscriber Flow

Expand All @@ -298,7 +298,7 @@

### Step 2 — Build a payment payload

Using Nevermined Payments libraries (Python or TS), you generate an x402 access token. The supported flow is **create-first**: create a delegation once with `createDelegation`, then request access tokens by passing its `delegationId`. A delegation captures the spending limit, duration, `provider`, and `currency`; reuse it for every token request until it expires or is exhausted.

Check warning on line 301 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L301

Did you really mean 'Nevermined'?

<Tabs>
<Tab title="TypeScript">
Expand Down Expand Up @@ -345,7 +345,7 @@
import base64
import json
import requests
from payments_py import Payments, PaymentOptions

Check warning on line 348 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L348

Did you really mean 'payments_py'?
from payments_py.x402 import (
CreateDelegationPayload,
DelegationConfig,
Expand Down Expand Up @@ -399,7 +399,7 @@
**The buyer-side `getX402AccessToken` / `get_x402_access_token` call does not auto-detect the scheme — it defaults to `nvm:erc4337` (crypto).** For a **fiat** plan (`isCrypto: false`) you must resolve and pass the scheme, and create the delegation with the matching card provider:

- Detect the scheme with `resolveScheme()` (TypeScript) / `resolve_scheme()` (Python), then pass `scheme: 'nvm:card-delegation'` to the token call.
- Use `provider: 'stripe'` (or `'braintree'` / `'visa'`) with `currency: 'usd'` in the delegation; for crypto plans use `provider: 'erc4337'` with `currency: 'usdc'`.

Check warning on line 402 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L402

Did you really mean 'crypto'?

Server-side **middleware** (Express, FastAPI) and the **A2A clients** resolve the scheme for you automatically — see the [Express.js](/integrate/add-to-your-agent/express) and [FastAPI](/integrate/add-to-your-agent/fastapi) guides, and [Which payment type does this plan need?](/getting-started/core-concepts#fiat-vs-crypto-what-each-plan-needs).
</Note>
Expand Down Expand Up @@ -541,7 +541,7 @@
</Tab>
<Tab title="Python">
```python
from payments_py import Payments, PaymentOptions

Check warning on line 544 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L544

Did you really mean 'payments_py'?

payments = Payments.get_instance(
PaymentOptions(nvm_api_key="nvm:agent-key", environment="sandbox")
Expand Down Expand Up @@ -581,11 +581,16 @@
maxAmount: BigInt(actualCreditsUsed),
})

// Return response with settlement receipt in payment-response header
// Return response with settlement receipt in payment-response header.
// Pass `billingModel` through: on a pay-as-you-go plan the credit fields are
// always "0", so without it the buyer cannot tell a real charge from a
// credits settle that burned nothing.
const settlementReceipt = Buffer.from(JSON.stringify({
success: true,
billingModel: settlement.billingModel,
creditsRedeemed: actualCreditsUsed,
transactionHash: settlement.txHash,
orderTx: settlement.orderTx,
transactionHash: settlement.transaction,
})).toString('base64')

res.set('payment-response', settlementReceipt)
Expand All @@ -603,11 +608,16 @@
max_amount=actual_credits_used,
)

# Return response with settlement receipt in payment-response header
# Return response with settlement receipt in payment-response header.
# Pass `billingModel` through: on a pay-as-you-go plan the credit fields are
# always "0", so without it the buyer cannot tell a real charge from a
# credits settle that burned nothing.
settlement_receipt = base64.b64encode(json.dumps({
"success": True,
"billingModel": settlement.billing_model,
"creditsRedeemed": actual_credits_used,
"transactionHash": settlement.tx_hash,
"orderTx": settlement.order_tx,
"transactionHash": settlement.transaction,
}).encode()).decode()

response.headers["payment-response"] = settlement_receipt
Expand All @@ -618,7 +628,7 @@

## Paying an MPP-protected service

Some services accept **MPP (Merchant Payment Protocol)** instead of x402. MPP settles against the same Nevermined Payment Plans as x402 — same credits, same delegation, same meter — so the buyer flow mirrors the one above with `/api/v1/mpp/*` in place of `/api/v1/x402/*`. You recognise an MPP service by its `402`: it carries a `WWW-Authenticate: Payment …` header instead of an x402 `accepts` body.

Check warning on line 631 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L631

Did you really mean 'Nevermined'?

<Note>
This is the **buyer** side. To make *your own* plan-protected endpoint MPP-payable, see [Accepting MPP payments](/products/payments-facilitator/mpp-seller).
Expand Down Expand Up @@ -654,7 +664,7 @@
<Tab title="Python">
```python
import os
from payments_py import Payments, PaymentOptions

Check warning on line 667 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L667

Did you really mean 'payments_py'?
from payments_py.mpp import MppFetchOptions
from payments_py.x402.types import DelegationConfig

Expand Down
2 changes: 1 addition & 1 deletion integrate/add-to-your-agent/express.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Express.js"
description: "Add Nevermined x402 payments to your Express.js application with one line of code"

Check warning on line 3 in integrate/add-to-your-agent/express.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/add-to-your-agent/express.mdx#L3

Did you really mean 'Nevermined'?
icon: "node-js"
frameworks: ["express", "node", "javascript"]
---
Expand Down Expand Up @@ -58,7 +58,7 @@

That's it! The middleware automatically:
- Returns `402` with `payment-required` header when no token is provided
- Verifies the x402 token via the Nevermined facilitator

Check warning on line 61 in integrate/add-to-your-agent/express.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/add-to-your-agent/express.mdx#L61

Did you really mean 'Nevermined'?
- Burns credits after request completion
- Returns `payment-response` header with settlement receipt

Expand Down Expand Up @@ -121,9 +121,9 @@
})
```

### Payment Scheme (Crypto vs. Fiat)

Check warning on line 124 in integrate/add-to-your-agent/express.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/add-to-your-agent/express.mdx#L124

Did you really mean 'Crypto'?

The middleware auto-detects the payment scheme from plan metadata. Plans with fiat pricing (`isCrypto: false`) automatically use `nvm:card-delegation` (Stripe), while crypto plans use `nvm:erc4337`.

Check warning on line 126 in integrate/add-to-your-agent/express.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/add-to-your-agent/express.mdx#L126

Did you really mean 'crypto'?

You can explicitly override the scheme in the route configuration:

Expand Down Expand Up @@ -169,7 +169,7 @@

// Hook after settlement
onAfterSettle: (req, creditsUsed, settlement) => {
console.log(`Settled ${creditsUsed} credits, tx: ${settlement.txHash}`)
console.log(`Settled ${creditsUsed} credits, tx: ${settlement.transaction}`)
},

// Custom error handler
Expand Down
10 changes: 10 additions & 0 deletions integrate/patterns/charge-credits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@

Copy-paste patterns for deducting credits from subscribers.

<Note>
**These patterns assume a `credits` plan.** A pay-as-you-go plan holds no credit balance — each
request is charged directly — so a successful settle there returns `creditsRedeemed: "0"` and
`remainingBalance: "0"`, with the charge referenced by `orderTx` (fiat rails) or `transaction`
(crypto rails). The settle response always carries `billingModel` (`credits` | `pay-as-you-go`);

Check warning on line 13 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L13

Did you really mean 'crypto'?
read it before either credit field. Gating on `creditsRedeemed > 0` alone reports a real
pay-as-you-go charge as a decline, and on a card rail that invites a retry of a payment that
already succeeded.
</Note>

## Automatic Credit Deduction

With the x402 flow, verification checks permissions without burning credits. After processing, settlement burns the credits:
Expand All @@ -21,7 +31,7 @@

// 1. Verify permissions (does NOT burn credits)
const verification = await payments.facilitator.verifyPermissions({
paymentRequired,

Check warning on line 34 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L34

Did you really mean 'paymentRequired'?
x402AccessToken: token,
maxAmount: BigInt(1)
})
Expand All @@ -45,7 +55,7 @@
</Tab>
<Tab title="Python">
```python
from payments_py.x402.helpers import build_payment_required

Check warning on line 58 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L58

Did you really mean 'payment_required'?

payment_required = build_payment_required(
plan_id=PLAN_ID, endpoint="/query", agent_id=AGENT_ID, http_verb="POST"
Expand Down Expand Up @@ -114,7 +124,7 @@
<Tab title="Python">
```python
def calculate_cost(request: dict) -> int:
base_credits = 1

Check warning on line 127 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L127

Did you really mean 'base_credits'?

# Add cost for complexity
if request.get('options', {}).get('high_quality'):
Expand Down Expand Up @@ -196,8 +206,8 @@
</Tab>
<Tab title="Python">
```python
from dataclasses import dataclass

Check warning on line 209 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L209

Did you really mean 'dataclasses'?
from datetime import datetime

Check warning on line 210 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L210

Did you really mean 'datetime'?
import uuid

@dataclass
Expand Down Expand Up @@ -294,7 +304,7 @@
</Tab>
<Tab title="Python">
```python
from dataclasses import dataclass

Check warning on line 307 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L307

Did you really mean 'dataclasses'?
from typing import List

@dataclass
Expand Down Expand Up @@ -353,7 +363,7 @@
async function processLongRunningTask(
x402Token: string,
paymentRequired: string,
estimatedCredits: number

Check warning on line 366 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L366

Did you really mean 'estimatedCredits'?
) {
// Step 1: Verify permissions (does not burn credits)
const verification = await payments.facilitator.verifyPermissions({
Expand Down Expand Up @@ -407,7 +417,7 @@
async def process_long_running_task(
x402_token: str,
payment_required: str,
estimated_credits: int

Check warning on line 420 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L420

Did you really mean 'estimated_credits'?
) -> dict:
# Step 1: Verify permissions (does not burn credits)
verification = payments.facilitator.verify_permissions(
Expand Down Expand Up @@ -486,7 +496,7 @@
</Tab>
<Tab title="Python">
```python
from fastapi import Response

Check warning on line 499 in integrate/patterns/charge-credits.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/charge-credits.mdx#L499

Did you really mean 'fastapi'?

def add_credit_headers(response: Response, credits: dict):
response.headers['X-Credits-Used'] = str(credits['used'])
Expand Down
19 changes: 17 additions & 2 deletions products/payments-facilitator/how-it-works.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
- [Making x402 programmable](https://nevermined.ai/blog/making-x402-programmable)
- [Building Agentic Payments with Nevermined, x402, A2A, and AP2](https://nevermined.ai/blog/building-agentic-payments-with-nevermined-x402-a2a-and-ap2)

## The Nevermined x402 programmable extension

Check warning on line 22 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L22

Did you really mean 'Nevermined'?

Nevermined extends x402 with the `nvm:erc4337` scheme, enabling **programmable settlement** (credits/subscriptions/PAYG) using ERC-4337 smart accounts and session keys.

Check warning on line 24 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L24

Did you really mean 'Nevermined'?

### PaymentRequired Response (402)

Expand Down Expand Up @@ -128,7 +128,7 @@

### Step 2: Generate x402 access token

Use the Nevermined SDK to generate an x402 access token. The supported flow is **create-first**: create a delegation once with `createDelegation` (`provider` and `currency` are required), then request access tokens by passing its `delegationId`. Reuse the delegation until it expires or is exhausted.

Check warning on line 131 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L131

Did you really mean 'Nevermined'?

<Tabs>
<Tab title="TypeScript">
Expand Down Expand Up @@ -157,7 +157,7 @@
<Tab title="Python">
```python
import os
from payments_py import Payments, PaymentOptions

Check warning on line 160 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L160

Did you really mean 'payments_py'?
from payments_py.x402 import (
CreateDelegationPayload,
DelegationConfig,
Expand Down Expand Up @@ -220,6 +220,8 @@
const settlement = JSON.parse(
Buffer.from(result.headers.get(X402_HEADERS.PAYMENT_RESPONSE)!, 'base64').toString()
)
// `billingModel` says how to read this receipt — see the note below
console.log('Billing model:', settlement.billingModel)
console.log('Credits used:', settlement.creditsRedeemed)
}
```
Expand All @@ -240,11 +242,24 @@
settlement = json.loads(
base64.b64decode(result.headers.get('payment-response')).decode()
)
# `billingModel` says how to read this receipt — see the note below
print('Billing model:', settlement.get('billingModel'))
print('Credits used:', settlement['creditsRedeemed'])
```
</Tab>
</Tabs>

<Note>
**A `creditsRedeemed` of `"0"` does not mean the call was free.** The settle receipt carries
`billingModel`: on a `credits` plan the payment shows up as `creditsRedeemed > 0`, but on a
**pay-as-you-go** plan there is no credit balance at all, so both `creditsRedeemed` and
`remainingBalance` are always the string `"0"` — even on a charge that succeeded. There the
proof is `success: true` plus a non-empty `orderTx` (fiat rails) or `transaction` (crypto

Check warning on line 257 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L257

Did you really mean 'crypto'?
rails). Gating on `creditsRedeemed > 0` alone reports a real charge as a decline, and on a card
rail that invites a retry of a payment that already went through. Note the values are
**strings**: `"0"` is truthy while `Number("0") > 0` is false.

Check warning on line 260 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L260

Did you really mean 'truthy'?
</Note>

## Resource server flow (API/agent side)

<Note>
Expand Down Expand Up @@ -341,7 +356,7 @@
</Tab>
<Tab title="Python">
```python
from payments_py import Payments, PaymentOptions

Check warning on line 359 in products/payments-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/payments-facilitator/how-it-works.mdx#L359

Did you really mean 'payments_py'?

payments = Payments.get_instance(
PaymentOptions(nvm_api_key=os.environ['NVM_API_KEY'], environment='sandbox')
Expand Down Expand Up @@ -385,7 +400,7 @@
const settlementReceipt = {
success: true,
creditsRedeemed: actualCreditsUsed,
transactionHash: settlement.txHash
transactionHash: settlement.transaction
}
res.set(X402_HEADERS.PAYMENT_RESPONSE, Buffer.from(JSON.stringify(settlementReceipt)).toString('base64'))

Expand All @@ -405,7 +420,7 @@
settlement_receipt = {
'success': True,
'creditsRedeemed': actual_credits_used,
'transactionHash': settlement.tx_hash
'transactionHash': settlement.transaction
}

response = jsonify({'result': result})
Expand Down
Loading