Skip to main content
Credit-card delegation is how a human user authorizes an AI agent to spend on their behalf, within an explicit budget and expiry, without handing over the card itself. The card is tokenized once via Stripe, Braintree, or Visa Intelligent Commerce, and the delegation defines exactly how much the agent can charge, for how long, and against which API key. Nevermined ships three ways to drive that flow, all built on the same backend. Pick the one that matches where your users live.

Directly in the App

Users go to nevermined.app, sign in, enroll a card, and create a delegation. Zero integration work.

White-label redirect (CLI)

Your CLI or backend prints a URL. The user clicks it, completes the flow in a chromeless page, and your process receives the IDs at a localhost callback.

Embedded widgets

Drop a Nevermined-hosted iframe straight into your own pricing page. Users never leave your domain.
Only the embedded widgets (option 3, iframe) are organization-scoped — they need an active Nevermined organization with a widget key. Options 1 and 2 work for anyone: option 1 for any signed-in user, and option 2 for any holder of an NVM API key. The redirect flow has an open path for autonomous agents (POST /api/v1/embed/session, no organization required) and an org-scoped path (POST /api/v1/widgets/session/self) used by the nvm CLI and org members.
Building an autonomous agent that needs to pay end to end? See AI Agents: Buy Access Autonomously for the full login → card → delegation → x402 path.

1. Directly in the Nevermined App

The fastest path for end-users and the natural fit when your users are already a part of the Nevermined community. No code, no integration.
1

Sign in to Nevermined

Open nevermined.app and sign in with email, Google, or GitHub. First-time users land on a short onboarding flow.
2

Open Settings > Payment Methods

In the Nevermined App, navigate to Settings > Payment Methods. This is the dashboard for every card you have enrolled and every active delegation.
3

Enroll a card

Click Enroll card and pick a provider tab: Stripe, Braintree, or Visa. Card data is captured in a PCI-compliant iframe and tokenized before submission — Nevermined never stores the raw card number.See Card Enrollment for the per-provider differences (Visa requires a passkey or OTP, Braintree supports PayPal alongside cards, Stripe needs 3DS where the issuer requires it).
4

Create a delegation

On the enrolled card row, click Delegate. Set:
  • Spending limit in dollars (minimum $0.50). The delegation is rejected once the agent has charged this amount in total.
  • Duration — 1 hour, 24 hours, 7 days, 30 days, or a custom value. The delegation expires automatically.
  • Max transactions (optional) — hard cap on how many times the agent can charge the card.
  • API key restriction (optional) — scope the delegation to a single API key so a compromised key can only spend the budget allowed to it.
See Mandates for the underlying lifecycle and revocation semantics.
5

Revoke at any time

Both cards and delegations can be revoked from the same dashboard. Revocation takes effect on the next agent request — no waiting period.

2. White-label redirect (CLI / top-level tab)

For tools that run outside the browser — CLIs, terminal-based agents, desktop apps — but still need to authorize a card. The pattern mirrors nvm login: print a URL, the user clicks it, the browser handles the chromeless card setup, and your process receives the result at a localhost callback.
This flow works with or without an organization, depending on which session you mint:
  • Autonomous agents / buyers (no org): mint an open embedded session with POST /api/v1/embed/session — any valid NVM API key, no organization needed. This is the path for AI agents purchasing on a user’s behalf.
  • Org members (e.g. the nvm CLI): mint an org-scoped session with POST /api/v1/widgets/session/self, which requires membership in the target organization.
Both produce a sessionToken of the same shape that the embed app accepts; what differs is who can mint it and what the card/delegation attach to — your own Nevermined account via the embedded path, or the organization via the widget path. See Platform Partners for the organization features.

How the handshake works

Agent / CLI            Nevermined API          Embed app (browser)    Local callback
 │                          │                     │                       │
 │ start one-shot           │                     │                       │
 │ bind /callback on        │                     │                       │
 │ 127.0.0.1:0 (random)     │                     │                       │──── listening
 │                          │                     │                       │
 │ POST /api/v1/embed/session                     │                       │
 │   Bearer <nvmApiKey>     │                     │                       │
 │   { returnUrl }          │                     │                       │
 │─────────────────────────▶│                     │                       │
 │◀─────────────────────────│ mint a session      │                       │
 │ { sessionToken,          │ bound to the        │                       │
 │   isReturnUrlAllowed }    │ caller's identity   │                       │
 │                          │ (no org needed)     │                       │
 │                          │                     │                       │
 │ open {embed}/cards/setup │                     │                       │
 │   ?sessionToken=…&returnUrl=…&state=<rand>&provider=stripe              │
 │────────────────────────────────────────────────▶│ chromeless page:    │
 │                          │                     │ - validate session    │
 │                          │                     │ - enroll card (Stripe │
 │                          │                     │   Elements / VGS /    │
 │                          │                     │   Braintree Drop-in)  │
 │                          │                     │ - create delegation   │
 │                          │                     │ - redirect to         │
 │                          │                     │   returnUrl + ids +   │
 │                          │                     │   state               │
 │                          │                     │──────────────────────▶│ GET /callback?paymentMethodId=…
 │◀──────────────────────────────────────────────────────── resolved &delegationId=…&state=…
 │                          │                     │                       │
 │ verify state matches     │                     │                       │
 │ → resolve, close         │                     │                       │
Org members (e.g. the nvm CLI) mint the session with POST /api/v1/widgets/session/self ({ orgId, returnUrl }) instead — same handshake, but it requires membership in the target organization.

The shortest possible integration

The Nevermined Payments CLI ships ready-made commands for this flow:
# Combined: enroll a card AND create a delegation. Returns both IDs.
nvm cards setup [--org <id>] [--provider stripe|braintree|visa]

# Single-purpose: only enroll a card.
nvm cards enroll [--org <id>] [--provider …]

# Single-purpose: only create a delegation against an already-enrolled card.
nvm cards delegate --card <paymentMethodId> [--org <id>]
Each command opens the browser at {embed}/cards/<route>?… — the standalone embed app (embed.nevermined.app for live, embed.nevermined.dev for staging), not the old webapp /embed/* routes — waits up to 5 minutes for the user to complete the flow, then exits cleanly with the resulting IDs. --no-browser prints the URL instead of opening it — useful over SSH or in CI.

Build your own (any language)

If you are writing a CLI in a language other than TypeScript, or you want this baked into your own backend, follow the four steps below.
1

Start a one-shot localhost callback server

Bind to 127.0.0.1:0 (the OS picks a free port). Listen on /callback. Note the port for step 3. Never bind to 0.0.0.0 — the callback should not be reachable from the network.
2

Mint a session

Authenticate the request with the user’s own NVM API key. Autonomous agents and buyers should use the open embedded session — it needs no organization:
POST {api}/api/v1/embed/session
Authorization: Bearer <nvmApiKey>
Content-Type: application/json

{
  "returnUrl": "http://localhost:54321/callback"
}
Response (200 OK):
{
  "sessionToken": "eyJ…",
  "userId": "us-…",
  "userWallet": "0x…",
  "apiKeyHash": "sandbox:eyJ…",
  "expiresAt": "2026-05-22T19:00:00.000Z",
  "isReturnUrlAllowed": true
}
The card and delegation attach to the caller’s own account. There is no org-membership check, so the failure modes are only:
HTTPMeaning
400returnUrl is not a valid http/https URL (DTO validation).
401 BCK.APIKEY.0004The NVM API key is invalid or expired — run nvm login again.
429Rate limited — the endpoint allows 30 mint calls per minute. Back off and retry.
Org members (and the nvm CLI) can instead mint an org-scoped session. It returns the same sessionToken shape, but requires membership in the target organization and takes an orgId:
POST {api}/api/v1/widgets/session/self
Authorization: Bearer <nvmApiKey>
Content-Type: application/json

{
  "orgId": "org-abc-123",
  "returnUrl": "http://localhost:54321/callback"
}
Additional failure codes: 403 BCK.WIDGET_SESSION.0018 (the caller has no active organization memberships — if you’re a buyer/agent enrolling a card, use the open POST /api/v1/embed/session flow above instead) and 403 BCK.WIDGET_SESSION.0019 (the orgId is not one of the caller’s memberships — list them via /organizations/my-memberships).
3

Open the browser

Construct the URL and open it with the OS browser-opener (xdg-open / open / start):
{embed}/cards/setup
  ?sessionToken=<token>
  &returnUrl=<urlEncoded callback>
  &state=<cryptographically random nonce>
  &provider=stripe
{embed} is the standalone embed app — https://embed.nevermined.app (live) or https://embed.nevermined.dev (staging). The card surfaces moved off the webapp /embed/* routes to this dedicated app. Three routes are available:
RouteCallback returns
{embed}/cards/setuppaymentMethodId + delegationId (one-shot, both steps)
{embed}/cards/enrollpaymentMethodId only
{embed}/cards/delegate?paymentMethodId=<id>delegationId only
The state value is echoed back in the redirect so you can bind the callback to this specific request (CSRF). 16 bytes (32 hex characters) of cryptographic randomness is the recommended minimum.
4

Receive the callback

The browser redirects to <returnUrl>?paymentMethodId=…&delegationId=…&state=<echo> on success.Your server must:
  1. Verify the state query parameter matches the one you issued. Compare in constant time (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). Reject mismatches with a 400.
  2. Extract the ID(s) from the query string.
  3. Respond 200 OK with a friendly “you can close this tab” HTML page.
  4. Close the server.
Recommended overall timeout: 5 minutes. Match the nvm login flow — users may switch contexts between the terminal and the browser.

Allowed returnUrl hosts

The server validates returnUrl against the session’s allow-list:
  • localhost, 127.0.0.1, [::1] (any port, any path) — accepted for both session kinds.
  • Other https:// origins — accepted only for widget-key sessions whose key has the origin in its allowedOrigins list.
  • Self-mint sessions — both the open POST /api/v1/embed/session and the org-scoped POST /api/v1/widgets/session/self documented above — reject any non-localhost host. Use the widget-key flow (see option 3) if you need remote redirects.

Security checklist

  • Bind only to 127.0.0.1, not 0.0.0.0.
  • Use a per-flow cryptographically random state. Compare in constant time.
  • Treat the session token like a bearer credential — don’t log it, don’t persist it beyond the flow. It carries apiKeyHash and expires in 2 hours.
  • The embed page strips sessionToken from the URL immediately after consumption, so the back button doesn’t replay it.
The full integrator reference, including TypeScript pseudocode, lives in cli-card-setup.md in the Payments SDK repo.

3. Embedded widgets (iframe)

Your end-users never leave your domain. You mint a widget session on your server with the organization’s widget-key and pass it straight to the browser SDK — no token-exchange round-trip — and the user completes the card-enrollment + delegation flow inside your own page.
The iframe widget flow requires an active Nevermined organization — this is the one option that does. The widget-key is the credential that scopes every iframe session to your org’s users — see Platform Partners for the upgrade path.
The full guide — including how to generate a widget key, mint widget sessions server-side, mount the iframe on your page, and react to nvm:success / nvm:error events — lives in Embed Nevermined Widgets. Choose this path when:
  • Your users are in a browser already (a website, a SaaS dashboard) and you want to keep them on your domain.
  • You need brand consistency — the iframe inherits the parent page’s framing and your organization’s branding theme.
  • You want a postMessage event stream rather than a one-shot redirect.
Choose the white-label redirect (option 2) instead when your tool runs outside a browser (CLI, agent, desktop) and you need a localhost callback.

Comparison

Directly in AppWhite-label redirectEmbedded widget
Where the user finishesnevermined.appA chromeless page in their browserYour own page (iframe)
Integration effortNoneOne HTTP server + one URL openA widget key + iframe mount
Requires an organizationNoNo (open embed session) — or org-scoped via the widget sessionYes
Best forPower users, internal teamsCLIs, terminal-based agents, desktop appsWebsites, SaaS dashboards
AuthenticationThe user’s own Privy loginThe user’s own NVM API key (Bearer)Your server-minted widget session
Result deliveryUI statepaymentMethodId + delegationId at a localhost callbacknvm:success postMessage to the parent page
Cross-origin redirectn/aLocalhost only (self-mint), or widget-key allowedOriginsSame as widget-key allowedOrigins

See Also