Cards
A SafeBank card is a debit card funded directly by a wallet (a Safe multisig). There is no float and no prefunded balance to top up: at authorization time the card draws USDC from the Safe on-chain. On top of that funding rail you attach software spend limits — per-transaction, daily, weekly, monthly, and MCC allow/block rules — that SafeBank evaluates on every authorization, most-restrictive-wins.
The limit windows are real, rolling accumulators (UTC day, ISO week, calendar month). An approved authorization advances them; a reversal credits them back. When a card has no limits configured, the Safe's on-chain allowance remains the only control — setting limits layers software policy on top.
Key endpoints
| Method | Path | Scope | Purpose |
|---|---|---|---|
POST | /v1/wallets/{walletId}/cards | cards:issue | Issue a Safe-funded card from a wallet (sandbox). |
GET | /v1/cards/{id}/limits | cards:read | Read a card's configured limits. |
PUT | /v1/cards/{id}/limits | cards:manage | Set limits (per-txn / daily / weekly / monthly / MCC). |
POST | /v1/cards/{id}/freeze | cards:manage | Freeze — authorizations decline immediately. |
POST | /v1/cards/{id}/unfreeze | cards:manage | Reactivate a frozen card. |
See the REST reference for full request/response schemas.
Issue a card
Issue a Safe-funded card straight from a wallet — the card draws USDC from that wallet's Safe. Seed its spend limits in the same call. Sandbox only for now (a Stripe cardholder is minted for your tenant automatically); live issuance requires KYB onboarding.
- curl
- TypeScript
- CLI
curl -X POST https://api.sandbox.safebank.ai/v1/wallets/w_1a2b3c/cards \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "dailyUsd": "500.00", "monthlyUsd": "5000.00" }'
const card = await sb.cards.issue('w_1a2b3c', { dailyUsd: '500.00', monthlyUsd: '5000.00' })
console.log(card.cardId, card.last4)
sb cards issue --wallet w_1a2b3c --daily 500 --monthly 5000
Setting limits
Every field on PUT /v1/cards/{id}/limits is optional — only the fields you
send are changed. Send an explicit null on a USD cap or on allowedMcc to
clear it (unlimited / no allowlist). USD values are decimal strings; MCC
entries are 2–4 digit code strings.
- curl
- TypeScript
- CLI
curl -X PUT https://api.sandbox.safebank.ai/v1/cards/card_1a2b3c/limits \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"perTransactionUsd": "250.00",
"dailyUsd": "500.00",
"weeklyUsd": "2000.00",
"monthlyUsd": "5000.00",
"blockedMcc": ["7995"],
"allowedMcc": ["5411", "5812"]
}'
import { SafeBank } from '@safebank/sdk'
const sb = new SafeBank({ apiKey: process.env.SAFEBANK_API_KEY! })
await sb.cards.setLimits('card_1a2b3c', {
perTransactionUsd: '250.00',
dailyUsd: '500.00',
weeklyUsd: '2000.00',
monthlyUsd: '5000.00',
blockedMcc: ['7995'],
allowedMcc: ['5411', '5812'],
})
const limits = await sb.cards.getLimits('card_1a2b3c')
console.log(limits.dailyUsd) // "500.00"
sb cards limits set card_1a2b3c \
--per-txn 250 --daily 500 --weekly 2000 --monthly 5000 \
--block-mcc 7995 --allow-mcc 5411,5812
sb cards limits show card_1a2b3c
What a limit breach looks like
When an authorization would exceed a cap or hit a blocked MCC, SafeBank declines it in real time and records the reason. The decline reasons are:
| Reason | Meaning |
|---|---|
PER_TXN_LIMIT | Amount exceeds the per-transaction cap. |
DAILY_CAP / WEEKLY_CAP / MONTHLY_CAP | The amount would push the rolling window over its cap. |
MCC_BLOCKED | The merchant category is on the block list. |
MCC_NOT_ALLOWED | An allowlist is set and the merchant category isn't on it. |
CARD_INACTIVE | The card is frozen. |
INSUFFICIENT_SAFE_BALANCE | The funding Safe can't cover the amount. |
Freeze and unfreeze
Freezing sets the card inactive at the issuer and in SafeBank, so
authorizations decline immediately (CARD_INACTIVE). Unfreeze reverses it —
freeze uses a reversible inactive state, never a terminal cancellation.
- curl
- TypeScript
- CLI
curl -X POST https://api.sandbox.safebank.ai/v1/cards/card_1a2b3c/freeze \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY"
curl -X POST https://api.sandbox.safebank.ai/v1/cards/card_1a2b3c/unfreeze \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY"
await sb.cards.freeze('card_1a2b3c')
// ...later
await sb.cards.unfreeze('card_1a2b3c')
sb cards freeze card_1a2b3c
sb cards unfreeze card_1a2b3c