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,9 +8,9 @@
- 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).
Expand All @@ -21,7 +21,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 24 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L24

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 @@ -32,7 +32,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 35 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L35

Did you really mean 'Nevermined's'?

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L35

Did you really mean 'Nevermined'?

## Background: What x402 Solves

Expand All @@ -47,21 +47,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 50 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L50

Did you really mean 'Nevermined'?

## Why Nevermined Extends x402

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L52

Did you really mean 'Nevermined'?

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

Nevermined introduces:

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L56

Did you really mean 'Nevermined'?

| Area | x402 | Nevermined |

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L58

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 64 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L64

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 @@ -83,17 +83,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 86 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L86

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

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L87

Did you really mean 'Blockchain'?


## The Nevermined x402 Extensions

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L90

Did you really mean 'Nevermined'?

Nevermined introduces two x402 schemes for different payment rails:

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L92

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 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 '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 @@ -119,7 +119,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 122 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L122

Did you really mean 'Crypto'?

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

## Complete Payment & Execution Flow

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

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L215

Did you really mean 'Nevermined'?

```mermaid
sequenceDiagram
Expand Down Expand Up @@ -275,7 +275,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 278 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L278

Did you really mean 'tx'?

## Developer Guide: Subscriber Flow

Expand All @@ -289,7 +289,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 292 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L292

Did you really mean 'Nevermined'?

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

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L339

Did you really mean 'payments_py'?
from payments_py.x402 import (
CreateDelegationPayload,
DelegationConfig,
Expand Down Expand Up @@ -390,7 +390,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 393 in development-guide/nevermined-x402.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L393

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 @@ -532,7 +532,7 @@
</Tab>
<Tab title="Python">
```python
from payments_py import Payments, PaymentOptions

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

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

development-guide/nevermined-x402.mdx#L535

Did you really mean 'payments_py'?

payments = Payments.get_instance(
PaymentOptions(nvm_api_key="nvm:agent-key", environment="sandbox")
Expand Down Expand Up @@ -572,11 +572,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 @@ -594,11 +599,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 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/x402-facilitator/how-it-works.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,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 18 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L18

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 20 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L20

Did you really mean 'Nevermined'?

### PaymentRequired Response (402)

Expand Down Expand Up @@ -124,7 +124,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 127 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L127

Did you really mean 'Nevermined'?

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

Check warning on line 156 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L156

Did you really mean 'payments_py'?
from payments_py.x402 import (
CreateDelegationPayload,
DelegationConfig,
Expand Down Expand Up @@ -216,6 +216,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 @@ -236,11 +238,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 253 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L253

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 256 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L256

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

## Resource server flow (API/agent side)

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

Check warning on line 355 in products/x402-facilitator/how-it-works.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

products/x402-facilitator/how-it-works.mdx#L355

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 @@ -381,7 +396,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 @@ -401,7 +416,7 @@
settlement_receipt = {
'success': True,
'creditsRedeemed': actual_credits_used,
'transactionHash': settlement.tx_hash
'transactionHash': settlement.transaction
}

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