Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nevermined.app/llms.txt

Use this file to discover all available pages before exploring further.

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.
Options 2 and 3 are organization-scoped — they require an active Nevermined organization with at least one member. Option 1 works for any signed-in user, with or without an organization.

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 path requires an active Nevermined organization with at least one member. The user driving the flow must be a member of that organization. Card setup is positioned as an organization feature — see Platform Partners to upgrade.

How the handshake works

CLI               Nevermined API           Browser              Local callback
 │                       │                     │                       │
 │ start one-shot        │                     │                       │
 │ bind /callback on     │                     │                       │
 │ 127.0.0.1:0 (random)  │                     │                       │──── listening
 │                       │                     │                       │
 │ POST /api/v1/widgets/ │                     │                       │
 │   session/self        │                     │                       │
 │   Bearer <nvmApiKey>  │                     │                       │
 │   { orgId, returnUrl }│                     │                       │
 │──────────────────────▶│                     │                       │
 │◀──────────────────────│ verify org          │                       │
 │ { sessionToken,       │ membership, mint    │                       │
 │   isReturnUrlAllowed }│ kind:'self-mint'    │                       │
 │                       │ widget session JWT  │                       │
 │                       │                     │                       │
 │ open                  │                     │                       │
 │ {frontend}/embed/     │                     │                       │
 │  cards/setup          │                     │                       │
 │  ?sessionToken=…      │                     │                       │
 │  &returnUrl=…         │                     │                       │
 │  &state=<rand>        │                     │                       │
 │  &provider=stripe     │                     │                       │
 │───────────────────────────────────────────▶│ chromeless page:      │
 │                       │                     │ - validate session     │
 │                       │                     │ - enroll card (Stripe │
 │                       │                     │   Elements / VGS /    │
 │                       │                     │   Braintree Drop-in)  │
 │                       │                     │ - create delegation   │
 │                       │                     │ - window.location     │
 │                       │                     │   .replace(returnUrl  │
 │                       │                     │   + ids + state)      │
 │                       │                     │──────────────────────▶│ GET /callback?paymentMethodId=…
 │◀──────────────────────────────────────────────────────────────resolved &delegationId=…&state=…
 │                       │                     │                       │
 │ verify state matches  │                     │                       │
 │ → resolve, close      │                     │                       │

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 {frontend}/embed/cards/<route>?…, 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 widget session

Authenticate the request with the user’s own NVM API key (the one they get from nvm login). The endpoint rejects callers who are not members of the requested organization.
POST {api}/api/v1/widgets/session/self
Authorization: Bearer <nvmApiKey>
Content-Type: application/json

{
  "orgId": "org-abc-123",
  "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
}
Failure codes:
HTTPError codeMeaning
401BCK.APIKEY.0004The NVM API key is invalid or expired. Run nvm login again.
403BCK.WIDGET_SESSION.0018The caller has no active organization memberships. Create or join one first.
403BCK.WIDGET_SESSION.0019The orgId in the request is not one of the caller’s memberships.
3

Open the browser

Construct the URL and open it with the OS browser-opener (xdg-open / open / start):
{frontend}/embed/cards/setup
  ?sessionToken=<token>
  &returnUrl=<urlEncoded callback>
  &state=<cryptographically random nonce>
  &provider=stripe
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 (the POST /api/v1/widgets/session/self path 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 session token on your server with the organization’s widget-key, the browser exchanges it for an iframe session, and the user completes the card-enrollment + delegation flow inside your own page.
Like option 2, the iframe widget flow requires an active Nevermined organization. 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 init-tokens 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 organizationNoYesYes
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 init-token
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