top of page

Looking To Hire Qualified Fintech Developers?

Stripe Issuing API for Fintech Businesses | FintegrationFS

Stripe Issuing API for Fintech Businesses | FintegrationFS

Build card programs with Stripe Issuing API — virtual & physical cards, spend controls & real-time authorization. FintegrationFS delivers Stripe Issuing integrations.

Stripe Issuing API: Build & Launch Card Programs in the US


Whether you're building a corporate expense tool, a B2B spend platform, or an embedded fintech wallet, the Stripe Issuing API gives US companies the infrastructure to create, manage, and control virtual and physical cards — all without becoming a card network themselves.


At FintegrationFS, we've integrated Stripe Issuing into production-grade fintech products for US startups and fintechs. This guide walks you through everything you need to know — from what the API actually does, to real code patterns and common mistakes to avoid.





What Is the Stripe Issuing API?


The Stripe Issuing API is a card-as-a-service platform that lets businesses programmatically issue Visa debit cards — virtual or physical — to their users or employees. You control the spend rules, authorization logic, and card lifecycle entirely through API calls and webhooks.


It's the backbone of card programs for expense management tools, neobanks, marketplace platforms, and B2B fintech products operating in the United States.


Stripe Issuing API: Core Objects You Must Understand


Before writing a single line of code, understand the three core building blocks:


Object

What It Represents

Key Fields

Cardholder

The person or business receiving the card

name, email, billing address, type (individual/company)

Card

The virtual or physical card issued

type (virtual/physical), currency, status, cardholder

Authorization

A real-time purchase request on the card

amount, merchant, approved, pending_request

Transaction

A settled charge after authorization

amount, card, merchant_data, type

Spend Controls

Rules restricting how the card can be used

spending_limits, allowed_categories, blocked_categories


US Use Cases for Stripe Issuing API


The Stripe Issuing API is purpose-built for modern US fintech products. Here are the most common real-world implementations:


Use Case

How Stripe Issuing Helps

Corporate Expense Cards

Issue per-employee cards with department-level spend limits

B2B Vendor Payments

Create single-use virtual cards for controlled vendor disbursements

Contractor/Marketplace Payouts

Issue cards to gig workers or sellers with merchant restrictions

Consumer Fintech Wallets

Virtual cards for secure online purchases with in-app controls

Travel & Fleet Management

Cards restricted to specific MCC codes (e.g., fuel, hotels)

Subscription Management Tools

Unique virtual cards per subscription to prevent overcharges


Stripe Issuing API: Full Integration Flow


A production Stripe Issuing integration follows this sequence:


Step 1 → Create a Cardholder Step 2 → Issue a Card (virtual or physical) Step 3 → Set Spend Controls Step 4 → Handle Real-Time Authorizations via Webhook Step 5 → Listen for Transaction & Lifecycle Events




Code Example 1: Create a Cardholder + Issue a Virtual Card (Node.js)


import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export async function createCardholderAndVirtualCard(req, res) {
  try {
    // Step 1: Create the Cardholder (KYC entity)
    const cardholder = await stripe.issuing.cardholders.create({
      type: "individual",           // or "company" for B2B
      name: req.body.name,
      email: req.body.email,
      phone_number: req.body.phone,
      billing: {
        address: {
          line1: req.body.address,
          city: req.body.city,
          state: req.body.state,
          postal_code: req.body.zip,
          country: "US",            // US-only for Stripe Issuing
        },
      },
      status: "active",
    });

    // Step 2: Issue a Virtual Card linked to this Cardholder
    const card = await stripe.issuing.cards.create({
      cardholder: cardholder.id,
      type: "virtual",
      currency: "usd",
      spending_controls: {
        spending_limits: [
          {
            amount: 100000,          // $1,000.00 in cents
            interval: "monthly",
          },
        ],
      },
    });

    // Return cardholder + card details to frontend
    res.status(201).json({
      cardholder_id: cardholder.id,
      card_id: card.id,
      card_last4: card.last4,
      card_status: card.status,
    });

  } catch (err) {
    console.error("Stripe Issuing error:", err.message);
    res.status(400).json({ error: err.message });
  }
}

Code Example 2: Real-Time Authorization Webhook (Node.js + Express)


This is the most critical part of a Stripe Issuing API integration. When a cardholder swipes their card, Stripe fires a synchronous webhook to your server — you have 2 seconds to approve or decline.


import express from "express";
import Stripe from "stripe";

const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// Raw body required for Stripe webhook signature verification
app.post(
  "/webhook/stripe-issuing",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const sig = req.headers["stripe-signature"];
    let event;

    // Step 1: Verify the webhook signature
    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        sig,
        process.env.STRIPE_ISSUING_WEBHOOK_SECRET
      );
    } catch (err) {
      console.error("Webhook signature failed:", err.message);
      return res.status(400).send("Webhook signature verification failed.");
    }

    // Step 2: Handle real-time authorization request
    if (event.type === "issuing_authorization.request") {
      const auth = event.data.object;
      const amountInDollars = auth.pending_request.amount / 100;
      const merchantCategory = auth.merchant_data.category;

      // Step 3: Apply your business logic
      const blockedCategories = ["gambling", "liquor_stores"];
      const isBlocked = blockedCategories.includes(merchantCategory);
      const exceedsLimit = amountInDollars > 500;

      const approved = !isBlocked && !exceedsLimit;

      // Step 4: Respond synchronously — must happen within 2 seconds
      return res.json({
        approved,
        reason: !approved
          ? isBlocked
            ? "merchant_blocked"
            : "amount_exceeds_limit"
          : undefined,
      });
    }

    // Step 5: Handle other async Issuing events
    switch (event.type) {
      case "issuing_authorization.created":
        console.log("Authorization created:", event.data.object.id);
        break;
      case "issuing_transaction.created":
        console.log("Transaction settled:", event.data.object.id);
        // Update your internal ledger here
        break;
      case "issuing_card.updated":
        console.log("Card status changed:", event.data.object.status);
        break;
      default:
        console.log("Unhandled event:", event.type);
    }

    res.json({ received: true });
  }
);

app.listen(3000, () => console.log("Stripe Issuing webhook server running"));

Code Example 3: Update Spend Controls on an Existing Card


// Restrict a card to specific merchant categories only (e.g., SaaS tools)
const updatedCard = await stripe.issuing.cards.update("ic_xxxxxxxxxxxxx", {
  spending_controls: {
    allowed_categories: ["computer_and_data_processing_services", "software_stores"],
    spending_limits: [
      {
        amount: 50000,   // $500.00
        interval: "per_authorization",
      },
    ],
  },
});

console.log("Card controls updated:", updatedCard.id);
```

---

## Stripe Issuing API: Key Webhook Events Reference

| Event | Trigger | Action Required |
|---|---|---|
| `issuing_authorization.request` | Card swiped (real-time) | Approve or decline **synchronously** within 2 seconds |
| `issuing_authorization.created` | Authorization created | Log to your DB |
| `issuing_authorization.updated` | Auth amount or status changed | Update internal record |
| `issuing_transaction.created` | Purchase settled | Update your ledger |
| `issuing_transaction.updated` | Transaction modified/reversed | Reconcile balance |
| `issuing_card.created` | New card issued | Notify cardholder |
| `issuing_card.updated` | Card status changed (e.g., frozen) | Update UI, alert user |
| `issuing_cardholder.created` | New cardholder added | Trigger KYC workflow if needed |

---

## Spend Control Options: What You Can Restrict

The **Stripe Issuing API** gives you granular control over how cards are used. Here's a breakdown:

| Control Type | Description | Example |
|---|---|---|
| **Spending Limits** | Cap amount per authorization, day, week, month, or year | $500/month per card |
| **Allowed Categories** | Whitelist specific MCC codes only | SaaS, travel, office supplies |
| **Blocked Categories** | Blacklist specific MCCs | Gambling, adult content, liquor |
| **Allowed Merchants** | Restrict to specific merchant IDs | Company-approved vendors only |
| **Blocked Merchants** | Block specific merchant IDs | Competitor platforms |

---

## Production-Ready Architecture for Stripe Issuing
```
┌─────────────────────────────────────────────────────┐
│                   Your Application                   │
│                                                      │
│   Frontend (React/Next.js)                           │
│   → Cardholder onboarding form                       │
│   → Card controls dashboard                          │
│   → Transaction history UI                           │
└──────────────────────┬──────────────────────────────┘
                       │ HTTPS
┌──────────────────────▼──────────────────────────────┐
│                   Backend API (Node.js)               │
│   → POST /cardholders  (create cardholder)           │
│   → POST /cards        (issue card)                  │
│   → PATCH /cards/:id   (update spend controls)       │
│   → GET  /transactions (fetch history)               │
└──────────┬────────────────────────┬─────────────────┘
           │                        │
┌──────────▼──────────┐   ┌─────────▼─────────────────┐
│   Stripe Issuing    │   │   Webhook Service          │
│   API               │   │   → /webhook/issuing       │
│   (api.stripe.com)  │   │   → Real-time auth logic   │
└─────────────────────┘   │   → Async event handlers   │
                          └────────────────────────────┘
                                     │
                          ┌──────────▼──────────────────┐
                          │   Database (PostgreSQL)      │
                          │   → cardholders table        │
                          │   → cards table              │
                          │   → transactions table       │
                          │   → webhook_events log       │
                          └─────────────────────────────┘

Common Mistakes When Integrating Stripe Issuing APIStripe Issuing vs. Alternatives: Quick Comparison


Feature

Stripe Issuing

Marqeta

Lithic

US Virtual Cards

✅ Yes

✅ Yes

✅ Yes

Physical Cards

✅ Yes

✅ Yes

✅ Yes

Real-Time Auth Webhooks

✅ Yes

✅ Yes

✅ Yes

Existing Stripe Ecosystem

✅ Native

❌ No

❌ No

Developer Documentation

⭐ Excellent

Good

Good

Best For

Startups + fintechs already on Stripe

Enterprise card programs

Developer-first teams






FAQ


Q1. What is the Stripe Issuing API and what can I build with it?

 

The Stripe Issuing API lets US businesses programmatically create and manage Visa debit cards — virtual or physical — with full control over spend limits, merchant restrictions, and authorization logic. Common builds include corporate expense tools, contractor payout cards, consumer wallets, and embedded finance products.


Q2. Does Stripe Issuing work for physical cards in the US? 


Yes. You can issue both virtual cards (available instantly via API) and physical cards (mailed to the cardholder's US address). Physical card requests require a billing address and shipping details at the time of creation.


Q3. How does real-time authorization work in Stripe Issuing? 


When a cardholder makes a purchase, Stripe sends a synchronous issuing_authorization.request webhook to your server. You must respond with approved: true or approved: false within 2 seconds. This is where you apply your spend policy, risk rules, or fraud logic.


Q4. What spend controls can I set on a Stripe Issuing card? 


You can restrict cards by spending amount (per authorization, daily, weekly, monthly, yearly), merchant category codes (MCCs), or specific merchant IDs. Controls can be applied at both the cardholder level and the individual card level.


Q5. Is Stripe Issuing available outside the US? 


Stripe Issuing is available in several countries, but the US program operates under US-specific regulatory requirements. Cards are issued in USD by Stripe's banking partners. Non-US deployments require separate Stripe Issuing access per region.


Q6. How do I securely display card numbers to users? 


Stripe provides ephemeral keys that allow your frontend to temporarily access sensitive card data (full PAN, CVV) directly from Stripe — without it ever passing through your server. This keeps you out of PCI scope for card number handling.


Q7. What database schema do I need for a Stripe Issuing integration? 


At minimum, you need tables for: cardholders (mapped to Stripe cardholder IDs), cards (mapped to Stripe card IDs + status), transactions (synced from Stripe webhooks), and webhook_events (for idempotency and debugging). Audit logs are strongly recommended for compliance.


Q8. Can I freeze or cancel a card via the Stripe Issuing API? 


Yes. You can update a card's status to inactive (frozen) or canceled at any time using the stripe.issuing.cards.update() method. Frozen cards can be reactivated; canceled cards cannot.


Q9. Do I need Stripe Treasury to use Stripe Issuing? 


Not necessarily. Stripe Issuing can operate with your own connected financial account. However, combining Stripe Issuing with Stripe Treasury (for balance management) gives you a more complete embedded finance stack if you need it.


Q10. How long does it take to go live with Stripe Issuing in the US? 


A well-scoped integration with a dedicated team takes 4–8 weeks for an MVP (virtual cards, basic controls, webhook handling). A full production build with physical cards, compliance workflows, and admin tooling typically takes 10–16 weeks.



Looking to build a Fintech Solution?

bottom of page