> ## 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.

# Card Delegation

> Let users enroll a credit or debit card once and delegate a spending budget to AI agents — directly in the app, via a white-labeled redirect, or embedded as a widget.

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.

<CardGroup cols={3}>
  <Card title="Directly in the App" icon="browser" href="#1-directly-in-the-nevermined-app">
    Users go to **nevermined.app**, sign in, enroll a card, and create a delegation. Zero integration work.
  </Card>

  <Card title="White-label redirect (CLI)" icon="terminal" href="#2-white-label-redirect-cli--top-level-tab">
    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.
  </Card>

  <Card title="Embedded widgets" icon="puzzle-piece" href="#3-embedded-widgets-iframe">
    Drop a Nevermined-hosted iframe straight into your own pricing page. Users never leave your domain.
  </Card>
</CardGroup>

<Note>
  Only the **embedded widgets** (option 3, iframe) are organization-scoped — they need an active [Nevermined organization](/docs/integrations/organizations) 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 `nevermined` CLI and org members.
</Note>

<Tip>
  Building an autonomous agent that needs to pay end to end? See [AI Agents: Buy Access Autonomously](/docs/getting-started/ai-agent-purchase) for the full login → card → delegation → x402 path.
</Tip>

## 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.

<Steps>
  <Step title="Sign in to Nevermined">
    Open [nevermined.app](https://nevermined.app) and sign in with email, Google, or GitHub. First-time users land on a short onboarding flow.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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. The provider must match the plan you intend to pay — [check it first with `resolveNetwork()` / `resolve_network()`](/docs/products/payments/card-enrollment#choosing-a-provider).

    See [Card Enrollment](/docs/products/payments/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).
  </Step>

  <Step title="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](/docs/products/payments/mandates) for the underlying lifecycle and revocation semantics.
  </Step>

  <Step title="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.
  </Step>
</Steps>

## 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 `nevermined 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.

<Note>
  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 `nevermined` 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](/docs/integrations/organizations) for the organization features.
</Note>

### 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 `nevermined` 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:

```bash theme={null}
# Combined: enroll a card AND create a delegation. Returns both IDs.
nevermined cards setup [--org <id>] [--provider stripe|braintree|visa]

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

# Single-purpose: only create a delegation against an already-enrolled card.
nevermined 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.

<Steps>
  <Step title="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.
  </Step>

  <Step title="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:

    ```http theme={null}
    POST {api}/api/v1/embed/session
    Authorization: Bearer <nvmApiKey>
    Content-Type: application/json

    {
      "returnUrl": "http://localhost:54321/callback"
    }
    ```

    Response (200 OK):

    ```json theme={null}
    {
      "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:

    | HTTP                  | Meaning                                                                          |
    | --------------------- | -------------------------------------------------------------------------------- |
    | 400                   | `returnUrl` is not a valid `http`/`https` URL (DTO validation).                  |
    | 401 `BCK.APIKEY.0004` | The NVM API key is invalid or expired — run `nevermined login` again.            |
    | 429                   | Rate limited — the endpoint allows 30 mint calls per minute. Back off and retry. |

    <Accordion title="Org-scoped alternative: POST /api/v1/widgets/session/self">
      Org members (and the `nevermined` 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`:

      ```http theme={null}
      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`).
    </Accordion>
  </Step>

  <Step title="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:

    | Route                                         | Callback returns                                          |
    | --------------------------------------------- | --------------------------------------------------------- |
    | `{embed}/cards/setup`                         | `paymentMethodId` + `delegationId` (one-shot, both steps) |
    | `{embed}/cards/enroll`                        | `paymentMethodId` 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.
  </Step>

  <Step title="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 `nevermined login` flow — users may switch contexts between the terminal and the browser.
  </Step>
</Steps>

### 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`](https://github.com/nevermined-io/payments/blob/main/markdown/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.

<Warning>
  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](/docs/integrations/organizations) for the upgrade path.
</Warning>

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](/docs/integrations/organization-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 App             | White-label redirect                                           | Embedded widget                              |
| ---------------------------- | --------------------------- | -------------------------------------------------------------- | -------------------------------------------- |
| **Where the user finishes**  | nevermined.app              | A chromeless page in their browser                             | Your own page (iframe)                       |
| **Integration effort**       | None                        | One HTTP server + one URL open                                 | A widget key + iframe mount                  |
| **Requires an organization** | No                          | No (open embed session) — or org-scoped via the widget session | Yes                                          |
| **Best for**                 | Power users, internal teams | CLIs, terminal-based agents, desktop apps                      | Websites, SaaS dashboards                    |
| **Authentication**           | The user's own Privy login  | The user's own NVM API key (Bearer)                            | Your server-minted widget session            |
| **Result delivery**          | UI state                    | `paymentMethodId` + `delegationId` at a localhost callback     | `nvm:success` postMessage to the parent page |
| **Cross-origin redirect**    | n/a                         | Localhost only (self-mint), or widget-key `allowedOrigins`     | Same as widget-key `allowedOrigins`          |

## See Also

* [Card Enrollment](/docs/products/payments/card-enrollment) — per-provider enrollment internals (Visa / Stripe / Braintree)
* [Mandates](/docs/products/payments/mandates) — what a delegation does once it exists
* [Embed Nevermined Widgets](/docs/integrations/organization-widgets) — full widget-key + iframe guide
* [x402 Card Delegation Spec](/docs/specs/x402-card-delegation) — protocol-level reference
* [Platform Partners](/docs/integrations/organizations) — how to become an organization
