
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.