API Overview
Programmatic access to SPEND.md through a RESTful API. Create wallets, manage cards, and monitor transactions from your applications.
Base URL
https://api.spend.md/v1Authentication
All API requests require authentication using an API key. Include the key in theAuthorization header:
Authorization: Bearer sk_live_your_api_key_hereGetting an API Key
Generate API keys from the CLI or dashboard:
$ spend api-key create --name "production"
✓ API Key created
Key: sk_live_abc123...xyz789
Name: production
Created: 2026-01-30
Expires: 2026-04-30
⚠️ Save this key securely. It won't be shown again.API Key Types
| Prefix | Environment | Use Case |
|---|---|---|
| sk_live_ | Production | Real transactions, live cards |
| sk_test_ | Test | Simulated transactions, no real money |
| sk_dev_ | Development | Local development, mock responses |
Request Format
All requests should include the following headers:
Content-Type: application/json
Authorization: Bearer sk_live_your_api_key
X-Idempotency-Key: unique-request-id # Recommended for mutationsIdempotency
For POST, PUT, and DELETE requests, include an X-Idempotency-Key header with a unique ID. This ensures requests are not duplicated if retried.
Endpoints
Wallets
| Method | Endpoint | Description |
|---|---|---|
| GET | /wallets | List all wallets |
| POST | /wallets | Create a new wallet |
| GET | /wallets/:id | Get wallet details |
| PATCH | /wallets/:id | Update wallet configuration |
| DELETE | /wallets/:id | Delete a wallet |
Cards
| Method | Endpoint | Description |
|---|---|---|
| GET | /wallets/:id/cards | List cards for a wallet |
| POST | /wallets/:id/cards | Create a virtual card |
| GET | /cards/:id | Get card details |
| POST | /cards/:id/freeze | Freeze a card |
| POST | /cards/:id/unfreeze | Unfreeze a card |
| DELETE | /cards/:id | Delete a card |
Transactions
| Method | Endpoint | Description |
|---|---|---|
| GET | /transactions | List transactions (with filters) |
| GET | /transactions/:id | Get transaction details |
| GET | /wallets/:id/transactions | List transactions for a wallet |
Approvals
| Method | Endpoint | Description |
|---|---|---|
| GET | /approvals | List pending approvals |
| POST | /approvals/:id/approve | Approve a transaction |
| POST | /approvals/:id/deny | Deny a transaction |
Example Requests
Create a Wallet
curl -X POST https://api.spend.md/v1/wallets \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"agent_name": "my-assistant",
"owner": "team@company.com",
"spend": {
"monthly_limit": { "amount": 500, "currency": "USD" },
"daily_limit": { "amount": 50, "currency": "USD" },
"allowlist": ["openai.com", "anthropic.com"]
}
}'Response
{
"id": "wal_abc123xyz",
"agent_name": "my-assistant",
"owner": "team@company.com",
"status": "active",
"balance": {
"amount": 0,
"currency": "USD"
},
"spend": {
"monthly_limit": { "amount": 500, "currency": "USD" },
"daily_limit": { "amount": 50, "currency": "USD" },
"spent_this_month": { "amount": 0, "currency": "USD" },
"spent_today": { "amount": 0, "currency": "USD" },
"allowlist": ["openai.com", "anthropic.com"]
},
"cards": [],
"created_at": "2026-01-30T12:00:00Z",
"updated_at": "2026-01-30T12:00:00Z"
}Create a Card
curl -X POST https://api.spend.md/v1/wallets/wal_abc123xyz/cards \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "primary",
"limit": { "amount": 100, "currency": "USD" }
}'List Transactions
curl "https://api.spend.md/v1/transactions?wallet=wal_abc123xyz&limit=10" \
-H "Authorization: Bearer sk_live_..."Webhooks
Receive real-time notifications about transactions, approvals, and other events via webhooks. Configure webhook URLs in your SPEND.md or via the API.
Webhook Payload
{
"id": "evt_abc123",
"type": "transaction.completed",
"created": "2026-01-30T14:23:45Z",
"data": {
"transaction": {
"id": "txn_xyz789",
"wallet_id": "wal_abc123xyz",
"card_id": "crd_def456",
"amount": { "amount": 12.50, "currency": "USD" },
"merchant": {
"name": "OpenAI",
"domain": "openai.com",
"category": "api_services"
},
"status": "completed",
"created_at": "2026-01-30T14:23:45Z"
}
}
}Verifying Webhooks
All webhooks include a signature in the X-Spend-Signature header. Verify this signature to ensure the webhook is authentic:
import crypto from 'crypto';
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}Error Handling
The API uses standard HTTP status codes and returns errors in a consistent format:
{
"error": {
"code": "insufficient_funds",
"message": "Transaction would exceed daily limit",
"details": {
"requested": { "amount": 75.00, "currency": "USD" },
"available": { "amount": 25.00, "currency": "USD" },
"limit": { "amount": 50.00, "currency": "USD" }
}
}
}Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (invalid parameters) |
| 401 | Unauthorized (invalid or missing API key) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not found |
| 422 | Unprocessable (business logic error) |
| 429 | Rate limited |
| 500 | Internal server error |
Rate Limits
API requests are rate limited based on your plan:
| Plan | Requests/minute | Requests/day |
|---|---|---|
| Free | 60 | 1,000 |
| Pro | 300 | 50,000 |
| Team | 1,000 | Unlimited |
Rate limit headers are included in every response: X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Reset.
SDKs & Libraries
Official SDKs are available for popular languages:
Node.js / TypeScript
npm install @spend/sdkPython
pip install spend-sdkGo
go get github.com/spend-md/goRuby
gem install spend-sdk