Skip to main content
Stripe

Stripe Issuing's standalone card platform → the Naive /cards primitive

Stripe Issuing lets you spin up virtual (and physical) Visa/Mastercard cards programmatically — create a Cardholder, issue a Card against it, set spending_controls, and watch authorizations and transactions stream in. It is a genuinely powerful card platform, and the API is excellent. But run directly, it is also a separate vendor account:
  • Cards live behind your Stripe secret key, Stripe dashboard, and Stripe’s Cardholder / Card object graph — funded from a pooled Issuing/Treasury balance you top up yourself.
  • A Stripe Cardholder scopes cards and nothing else. It is a KYC entity for issuing — it has no idea about the agent’s inbox, its vault secrets, its OAuth connections, or any other KYC you ran.
  • “Who let this agent issue a $5k card, and what else can this agent touch?” is answered in Stripe for cards, and in unrelated systems for everything else. There is no shared accountability.
Naive’s /cards primitive gives the agent the same capability — full Visa/Mastercard virtual cards via the managed_virtual provider — but rooted in one identity:
  • The tenant user that holds a card is the same user that owns its email inbox, its vault secrets, its KYC, and its connections.
  • Whether an agent may issue or fund a card — and up to what total — is decided by that user’s Account Kit at execution time, not by trusting your own code to check first.
  • Every cardholder create, card issue, top-up, and transaction lands in the same per-user activity log as everything else the agent does.
This guide maps Stripe Issuing’s API to Naive’s, shows the smallest working swap, and is explicit about what does not map yet (Stripe Issuing has real per-authorization controls that Naive does not match today — read the gaps before you commit).
Stripe and related marks are trademarks of Stripe, Inc., used here for identification only. Naive is not affiliated with or endorsed by Stripe; card funding may use hosted checkout, but this guide does not describe Stripe’s internal Issuing implementation.
Tested against: Stripe Node SDK stripe v17 (Issuing API, versions 2025-12-15 / 2026-04-22, per docs.stripe.com/api/issuing, snapshot June 2026) and the Naive API (base https://api.usenaive.ai/v1, docs snapshot June 2026) plus the Naive Node SDK @usenaive-sdk/server.Version notes:
  • Naive’s managed_virtual provider issues a full Visa/Mastercard virtual card (cardholder required, no spending cap); the default prepaid_gift provider is a prepaid Visa gift card (capped at $150, no cardholder). This guide targets managed_virtual, the closest match to a direct Stripe Issuing card.
  • Stripe Issuing card credentials (number, cvc) are retrieved via cards.retrieve(id, { expand }) and require your platform to be PCI-compliant or to use Stripe Elements. Naive returns them from GET /v1/cards/:id/details once the card is active.
  • Both are evolving APIs — verify method names and the funding model against your installed versions.

Concept map

Before / after: the core path

The path that matters for almost every agent that spends money is register a cardholder, issue a card, then read its credentials. Here it is on both platforms.
The cardholder → card → credentials shape lines up closely. The real differences to plan for:
  • Funding model. Stripe Issuing typically draws from a pooled Issuing/Treasury balance you top up. Naive funds each card through a hosted checkout (checkout_url) for the card’s amount, then issues it on check-payment. There is no shared balance to manage.
  • Spend limits. Stripe’s spending_controls can include per-interval and per-MCC rules. Naive’s spending_limit_cents is a single funded cap per card — simpler, but not category- or interval-aware. See the gaps.
  • The cardholder is your identity, not a standalone KYC object. In Stripe a Cardholder exists only for issuing. In Naive the cardholder hangs off a tenant user that also owns the agent’s inbox, vault, connections, and KYC.

Funding and issuing

Both platforms separate “create the card” from “the card can spend.” Stripe does it with a balance; Naive does it with a per-card checkout.
  • On Naive, issuing a card, creating a cardholder, and topping up are sensitive and may be approval-gated by the Account Kit — the call can return 202 { status: "pending_approval", approval_id } until a human approves, then the API replays it.
  • A card’s status moves pending_payment → active after check-payment confirms funding. See Card statuses.

Minimal viable migration

The smallest swap that keeps a spending agent running is cardholder → issue → reveal. You do not need to recreate Treasury, per-MCC controls, or Account Kits to make your first card.
1

Install the SDK and set your key

Set NAIVE_API_KEY (a server-side key from the dashboard).
2

Map your users

A Stripe Cardholder becomes a Naive tenant user with a cardholder attached. Reuse your own database id as external_id so the mapping is 1:1, or pass it straight into naive.forUser(id).
3

Recreate the cardholder once per identity

Replace stripe.issuing.cardholders.create({ name, billing }) with POST /v1/cards/cardholder (firstName, lastName, billing…, dob…). One cardholder per tenant user covers all of that user’s managed_virtual cards.
4

Swap card issuance

Replace stripe.issuing.cards.create with client.cards.create({ name, spending_limit_cents, provider: "managed_virtual" }), open the returned checkout_url, then check-payment to issue.
5

Read credentials and ship

Replace cards.retrieve(id, { expand:["number","cvc"] }) with GET /v1/cards/:id/details. At this point you are off your direct Stripe Issuing integration for the core path. Everything below is upside, not a requirement.

Consolidate further once you’re on Naive

This is where the migration pays for itself. With Stripe Issuing direct, a Cardholder isolates cards and nothing else. On Naive, the unit of isolation is a tenant user, and it isolates the agent’s entire footprint.

Gain #1 — one identity across primitives

  • With Stripe Issuing, the agent’s cards are an island. With Naive, naive.forUser(acme.id) is a single handle to cards and email and vault and connections and KYC.
  • You provision a customer’s whole agent footprint from one identity, and tear it down from one place. Sibling tenants can never read each other’s cards, secrets, or inboxes.

Gain #2 — execution-time permission enforcement

  • Whether an agent may issue or fund a card is policy on the Account Kit — not a check you hand-write, and not “we trust the agent to call the right function.”
  • The agent’s code is identical for every tier — client.cards.create({ … }). Whether the call runs is decided by the caller’s kit at execution time:
    • primitives_config.cards.enabled: false → the exact same line returns forbidden, no code change.
    • requiresApproval: true → issuing, cardholder creation, and top-ups freeze as a pending approval; the API replays them only after a human approves.
    • The funded spending_limit_cents caps how much was loaded onto the card — spend is generally bounded by that funded amount, though treat issuer/network behavior as authoritative for edge cases.
  • Stripe gates who has Issuing access at the Connect-account level and per-authorization via your own webhook logic. Naive additionally gates the issuance and funding decision per tenant user, declaratively, with no per-swipe handler to run. (Per-authorization MCC control is still a Stripe strength — see gaps.)

Gain #3 — unified accountability

  • Every cardholder create, card issue, top-up, and transaction for a customer lands in one per-user activity log — alongside their email, vault, and connection events, not in a separate Stripe dashboard:
  • That is the question that is hard to answer when cards live in Stripe, email lives in another vendor, and secrets live somewhere else. Under Naive it is a single query.

What does not map yet

A migration guide that hides gaps is worse than none. The core path (cardholder → issue → reveal → transactions → cancel) maps cleanly, but Stripe Issuing has real capabilities Naive’s /cards does not match today. Check this list against your app before you commit — some of these are why you might stay on Stripe Issuing direct.
If your agent relies on real-time per-authorization decisions, MCC/interval spending controls, physical cards, or disputes, those are the gaps most likely to block you — keep those workloads on Stripe Issuing direct for now. The cardholder → issue → fund → reveal → transactions loop, with Account-Kit governance over who can issue and fund — the most common agent-spend pattern — maps cleanly today.

Where to go next