Issue and Manage a Card
Issue virtual and physical payment cards linked to user wallets.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Authentication | How to obtain access tokens |
| Onboarding | User and wallet registration |
| KYC | KYC verification requirements |
Overview
Wirex BaaS supports issuing Visa payment cards linked to user Smart Wallets. Cards can be:
- Virtual — Digital cards for online payments, issued instantly
- Physical — Plastic cards delivered to the user's address
Cards are funded from the user's unified balance (WUSD/WEUR) and can be used anywhere Visa is accepted.
Prerequisites
Before issuing a card, check user eligibility via the user endpoint. Capabilities are returned as part of the user response.
GET /api/v2/user
const response = await fetch(`${baseUrl}/api/v2/user`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
const user = await response.json();
const virtualCardCapability = user.capabilities.find(c => c.type === 'VisaVirtualCard');
if (virtualCardCapability?.status === 'Active') {
// User can issue virtual cards
}import requests
response = requests.get(
f"{base_url}/api/v2/user",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
user = response.json()
virtual_card = next((c for c in user["capabilities"] if c["type"] == "VisaVirtualCard"), None)
if virtual_card and virtual_card["status"] == "Active":
# User can issue virtual cards
passreq, _ := http.NewRequest("GET", baseURL+"/api/v2/user", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var user UserResponse
json.NewDecoder(resp.Body).Decode(&user)
for _, cap := range user.Capabilities {
if cap.Type == "VisaVirtualCard" && cap.Status == "Active" {
// User can issue virtual cards
}
}Response (relevant fields):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"owner": "0x1234567890abcdef1234567890abcdef12345678",
"verification": {
"status": "Approved",
"levels": [
{
"status": "Approved",
"type": "SDD",
"name": "Wirex SDD Level"
}
]
},
"capabilities": [
{
"type": "VisaVirtualCard",
"verification_requirements": [
{ "type": "BDD", "order": 1 }
],
"prerequisites": null,
"status": "Active"
},
{
"type": "VisaPlasticCard",
"verification_requirements": [
{ "type": "SDD", "order": 1 }
],
"prerequisites": null,
"status": "Active"
}
]
}Card Capabilities
| Capability | Description |
|---|---|
VisaVirtualCard | Issue virtual Visa cards |
VisaPlasticCard | Issue physical Visa cards |
Capability Fields
| Field | Description |
|---|---|
type | Capability identifier |
status | Current status (see below) |
status_reason | Explanation when status is not Active |
verification_requirements | KYC levels required for this capability |
prerequisites | Other capabilities that must be active first |
Capability Status
| Status | Description | Action |
|---|---|---|
Active | Ready to issue cards | Proceed with issuance |
NotFulfilled | Requirements not met | Check status_reason for details |
NotAvailable | Not available for user/region | Cannot issue this card type |
Example: NotFulfilled Due to Verification Level
{
"type": "VisaPlasticCard",
"verification_requirements": [
{ "type": "SDD", "order": 1 }
],
"prerequisites": null,
"status": "NotFulfilled",
"status_reason": "Verification level SDD is required"
}The user has completed BDD verification but VisaPlasticCard requires SDD level. The user must complete SDD verification before ordering a plastic card
Issue Virtual Card
Virtual card issuance may require fee payment depending on your company configuration. Follow these steps:
Step 1: Check Order Fees
GET /api/v1/cards/Virtual/fees/{country}
| Parameter | Description |
|---|---|
country | User's country (ISO 3166-1 alpha-2, e.g., GB) |
Response:
{
"currency": "USD",
"order_fee": 9.99,
"estimated_payment_amounts": [
{
"amount": 10.05,
"precise_amount": "10050000",
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"token_symbol": "USDC",
"rate": 0.9940
}
]
}| Field | Description |
|---|---|
order_fee | Fee amount in fiat currency |
estimated_payment_amounts | Fee converted to supported tokens |
If the endpoint returns a 400 error with "Card fees are not enforced", fees are not applicable for your company — skip to Step 3.
const feesResponse = await fetch(`${baseUrl}/api/v1/cards/Virtual/fees/${country}`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
if (feesResponse.status === 400) {
// Fees not enforced, skip to issuing card
} else {
const fees = await feesResponse.json();
// Proceed to create fees invoice
}response = requests.get(
f"{base_url}/api/v1/cards/Virtual/fees/{country}",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
if response.status_code == 400:
# Fees not enforced, skip to issuing card
pass
else:
fees = response.json()
# Proceed to create fees invoicereq, _ := http.NewRequest("GET", baseURL+"/api/v1/cards/Virtual/fees/"+country, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
if resp.StatusCode == 400 {
// Fees not enforced, skip to issuing card
} else {
var fees FeesResponse
json.NewDecoder(resp.Body).Decode(&fees)
// Proceed to create fees invoice
}Step 2: Create Fees Invoice (If Required)
POST /api/v2/cards/Virtual/fees/{country}/payment
Request:
{
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}| Field | Type | Required | Description |
|---|---|---|---|
token_address | string | Yes | Token to pay fees with |
Response:
{
"delivery_id": 12345,
"recipient_address": "0x...",
"payment_amount": {
"amount": 10.05,
"precise_amount": "10050000",
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"token_symbol": "USDC",
"rate": 0.9940
}
}The fee is deducted from the user's unified balance automatically when issuing the card.
const invoiceResponse = await fetch(`${baseUrl}/api/v2/cards/Virtual/fees/${country}/payment`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
token_address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
})
});
const invoice = await invoiceResponse.json();
const deliveryId = invoice.delivery_id;response = requests.post(
f"{base_url}/api/v2/cards/Virtual/fees/{country}/payment",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id,
"Content-Type": "application/json"
},
json={"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}
)
invoice = response.json()
delivery_id = invoice["delivery_id"]body, _ := json.Marshal(map[string]string{
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
})
req, _ := http.NewRequest("POST", baseURL+"/api/v2/cards/Virtual/fees/"+country+"/payment", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var invoice InvoiceResponse
json.NewDecoder(resp.Body).Decode(&invoice)
deliveryId := invoice.DeliveryIdStep 3: Issue Card
POST /api/v2/cards/virtual
Headers (S2S example):
Authorization: Bearer {access_token}X-User-Address: {user_eoa_address}X-Chain-Id: {chain_id}
For other authentication methods, see Authentication.
Request:
{
"delivery_id": 12345,
"card_name": "My Shopping Card",
"name_on_card": "Alex Grey"
}| Field | Type | Required | Description |
|---|---|---|---|
delivery_id | integer | Conditional | Required if fees were created in Step 2 |
card_name | string | No | Display name for the card (1-50 chars) |
name_on_card | string | No | Name printed on card (2-26 chars, letters only) |
Response:
{
"id": "64120850-73a1-4df5-a074-d463258c9deb"
}The card is created in Requested status and transitions to NotActivated once processed.
const issueResponse = await fetch(`${baseUrl}/api/v2/cards/virtual`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
delivery_id: deliveryId, // omit if fees not enforced
card_name: 'My Shopping Card',
name_on_card: 'Alex Grey'
})
});
const card = await issueResponse.json();
console.log('Card ID:', card.id);response = requests.post(
f"{base_url}/api/v2/cards/virtual",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id,
"Content-Type": "application/json"
},
json={
"delivery_id": delivery_id, # omit if fees not enforced
"card_name": "My Shopping Card",
"name_on_card": "Alex Grey"
}
)
card = response.json()
print("Card ID:", card["id"])body, _ := json.Marshal(map[string]interface{}{
"delivery_id": deliveryId, // omit if fees not enforced
"card_name": "My Shopping Card",
"name_on_card": "Alex Grey",
})
req, _ := http.NewRequest("POST", baseURL+"/api/v2/cards/virtual", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var card CardResponse
json.NewDecoder(resp.Body).Decode(&card)
fmt.Println("Card ID:", card.Id)Issue Physical Card
Physical card issuance requires checking delivery availability and selecting a delivery method.
Step 1: Get Delivery Countries
GET /api/v1/cards/delivery/countries
Response:
["GB", "DE", "FR", "ES", "IT"]const countriesResponse = await fetch(`${baseUrl}/api/v1/cards/delivery/countries`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
const countries = await countriesResponse.json();response = requests.get(
f"{base_url}/api/v1/cards/delivery/countries",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
countries = response.json()req, _ := http.NewRequest("GET", baseURL+"/api/v1/cards/delivery/countries", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var countries []string
json.NewDecoder(resp.Body).Decode(&countries)Step 2: Get Delivery Methods
GET /api/v1/cards/delivery/methods/{country}
| Parameter | Description |
|---|---|
country | ISO 3166-1 alpha-2 code from Step 1 |
Response:
[
{
"fee": 15.00,
"currency": "USD",
"provider": "DHL",
"estimated_payment_amounts": [
{
"amount": 15.10,
"precise_amount": "15100000",
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"token_symbol": "USDC",
"rate": 0.9934
}
]
}
]| Field | Description |
|---|---|
fee | Delivery fee in fiat currency |
currency | Fiat currency for the fee |
provider | Courier provider (use in Step 4 and Step 5) |
estimated_payment_amounts | Fee converted to supported tokens |
Display available methods to the user and use the selected provider value in Step 4 and Step 5.
const methodsResponse = await fetch(`${baseUrl}/api/v1/cards/delivery/methods/${country}`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
const methods = await methodsResponse.json();
const selectedProvider = methods[0].provider; // e.g., 'DHL'response = requests.get(
f"{base_url}/api/v1/cards/delivery/methods/{country}",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
methods = response.json()
selected_provider = methods[0]["provider"] # e.g., 'DHL'req, _ := http.NewRequest("GET", baseURL+"/api/v1/cards/delivery/methods/"+country, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var methods []DeliveryMethod
json.NewDecoder(resp.Body).Decode(&methods)
selectedProvider := methods[0].Provider // e.g., "DHL"Step 3: Check Order Fees
GET /api/v1/cards/Plastic/fees/{country}
See Issue Virtual Card for response format.
Step 4: Create Fees Invoice (If Required)
POST /api/v2/cards/Plastic/fees/{country}/payment
Request:
{
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"delivery_provider": "DHL"
}| Field | Type | Required | Description |
|---|---|---|---|
token_address | string | Yes | Token to pay fees with |
delivery_provider | string | Yes | Provider from Step 2 |
Step 5: Issue Card
POST /api/v2/cards/plastic
Request:
{
"delivery_id": 12345,
"delivery_provider": "DHL",
"card_name": "My Travel Card",
"name_on_card": "Alex Grey",
"delivery_address": {
"line1": "10 Downing Street",
"line2": "Flat 2",
"city": "London",
"state": "",
"zip_code": "SW1A 2AA",
"country": "GB"
}
}| Field | Type | Required | Description |
|---|---|---|---|
delivery_id | integer | Conditional | Required if fees were created in Step 4 |
delivery_provider | string | No | Courier provider from Step 2 |
card_name | string | No | Display name (1-50 chars) |
name_on_card | string | No | Name printed on card (2-26 chars) |
delivery_address | object | Yes | Shipping address |
Delivery Address Fields
| Field | Type | Required | Description |
|---|---|---|---|
line1 | string | Yes | Street address |
line2 | string | No | Apartment, suite |
city | string | Yes | City name |
state | string | No | State or province |
zip_code | string | Yes | Postal/ZIP code |
country | string | Yes | ISO 3166-1 alpha-2 code |
Response:
{
"id": "64120850-73a1-4df5-a074-d463258c9deb"
}const issueResponse = await fetch(`${baseUrl}/api/v2/cards/plastic`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
delivery_id: deliveryId,
delivery_provider: 'DHL',
card_name: 'My Travel Card',
name_on_card: 'Alex Grey',
delivery_address: {
line1: '10 Downing Street',
line2: 'Flat 2',
city: 'London',
state: '',
zip_code: 'SW1A 2AA',
country: 'GB'
}
})
});
const card = await issueResponse.json();response = requests.post(
f"{base_url}/api/v2/cards/plastic",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id,
"Content-Type": "application/json"
},
json={
"delivery_id": delivery_id,
"delivery_provider": "DHL",
"card_name": "My Travel Card",
"name_on_card": "Alex Grey",
"delivery_address": {
"line1": "10 Downing Street",
"line2": "Flat 2",
"city": "London",
"state": "",
"zip_code": "SW1A 2AA",
"country": "GB"
}
}
)
card = response.json()body, _ := json.Marshal(map[string]interface{}{
"delivery_id": deliveryId,
"delivery_provider": "DHL",
"card_name": "My Travel Card",
"name_on_card": "Alex Grey",
"delivery_address": map[string]string{
"line1": "10 Downing Street",
"line2": "Flat 2",
"city": "London",
"state": "",
"zip_code": "SW1A 2AA",
"country": "GB",
},
})
req, _ := http.NewRequest("POST", baseURL+"/api/v2/cards/plastic", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var card CardResponse
json.NewDecoder(resp.Body).Decode(&card)Card Lifecycle
Requested → NotActivated → Active
↓
Blocked ←→ Active
↓
Closed
| Status | Description |
|---|---|
Requested | Card order submitted, processing |
NotActivated | Card issued, awaiting activation |
Active | Card is active and usable |
Blocked | Temporarily blocked (can be unblocked) |
Closed | Permanently closed |
Activate Card
Activation is required only for physical cards. This protects user funds if the card is stolen during shipment.
PUT /api/v1/cards/{cardId}/activate
Request:
{
"card_number_last4": "1234",
"expiry_date": "12/2027"
}| Field | Type | Required | Description |
|---|---|---|---|
card_number_last4 | string | Yes | Last 4 digits of card number |
expiry_date | string | Yes | Expiration date in MM/YYYY format |
Response: Empty on success (200 OK)
await fetch(`${baseUrl}/api/v1/cards/${cardId}/activate`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
card_number_last4: '1234',
expiry_date: '12/2027'
})
});requests.put(
f"{base_url}/api/v1/cards/{card_id}/activate",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id,
"Content-Type": "application/json"
},
json={
"card_number_last4": "1234",
"expiry_date": "12/2027"
}
)body, _ := json.Marshal(map[string]string{
"card_number_last4": "1234",
"expiry_date": "12/2027",
})
req, _ := http.NewRequest("PUT", baseURL+"/api/v1/cards/"+cardId+"/activate", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)Get Cards
Retrieve all cards for a user.
GET /api/v1/cards
const response = await fetch(`${baseUrl}/api/v1/cards`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
const { data: cards } = await response.json();response = requests.get(
f"{base_url}/api/v1/cards",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
cards = response.json()["data"]req, _ := http.NewRequest("GET", baseURL+"/api/v1/cards", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result CardsResponse
json.NewDecoder(resp.Body).Decode(&result)
cards := result.DataResponse:
{
"data": [
{
"id": "64120850-73a1-4df5-a074-d463258c9deb",
"card_wallet_address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874",
"status": "Active",
"status_reason": "",
"previous_status": "NotActivated",
"generation": "Gen2",
"provider": "Wirex",
"limit": {
"daily_limit": 10000.00,
"daily_usage": 250.00,
"currency": "USD"
},
"allowed_actions": [
{ "type": "Block", "relative_path": "/api/v1/cards/:cardId/block" },
{ "type": "Close", "relative_path": "/api/v1/cards/:cardId/close" },
{ "type": "SetLimit", "relative_path": "/api/v1/cards/:cardId/limit" },
{ "type": "GetDetails", "relative_path": "/api/v1/cards/:cardId/details" },
{ "type": "GetCvv", "relative_path": "/api/v1/cards/:cardId/cvv" }
],
"card_data": {
"card_name": "My Card",
"name_on_card": "Alex Grey",
"card_number_last_4": "0333",
"expiry_date": "12/2027",
"payment_system": "Visa",
"format": "Virtual"
},
"created_at": "2024-01-01T10:00:00Z",
"updated_at": "2024-01-01T10:05:00Z"
}
]
}Allowed Action Types
| Type | Description |
|---|---|
Activate | Activate the card |
Block | Block the card |
Unblock | Unblock the card |
Close | Close the card |
SetLimit | Change card limits |
GetDetails | Get full card details (PAN) |
GetCvv | Get card CVV |
GetPin | Get card PIN |
Card Data Visibility
| Field | Visible When |
|---|---|
card_number_first_4 | Status is Requested or NotActivated |
card_number_last_4 | Status is Active, Blocked, or Closed |
expiry_date | Status is Active, Blocked, or Closed |
Manage Cards
Block Card
Temporarily block a card. Can be unblocked later.
PUT /api/v1/cards/{cardId}/block
Unblock Card
Restore a blocked card to active status.
PUT /api/v1/cards/{cardId}/unblock
Close Card
Permanently close a card. This action cannot be undone.
PUT /api/v1/cards/{cardId}/close
// Block
await fetch(`${baseUrl}/api/v1/cards/${cardId}/block`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
// Unblock
await fetch(`${baseUrl}/api/v1/cards/${cardId}/unblock`, {
method: 'PUT',
headers: { /* same headers */ }
});
// Close
await fetch(`${baseUrl}/api/v1/cards/${cardId}/close`, {
method: 'PUT',
headers: { /* same headers */ }
});headers = {
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
# Block
requests.put(f"{base_url}/api/v1/cards/{card_id}/block", headers=headers)
# Unblock
requests.put(f"{base_url}/api/v1/cards/{card_id}/unblock", headers=headers)
# Close
requests.put(f"{base_url}/api/v1/cards/{card_id}/close", headers=headers)actions := []string{"block", "unblock", "close"}
for _, action := range actions {
req, _ := http.NewRequest("PUT", baseURL+"/api/v1/cards/"+cardId+"/"+action, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
http.DefaultClient.Do(req)
}Webhooks
Card status changes trigger webhook notifications. The payload structure matches the Get Cards response.
Card Status Webhook
Endpoint: POST {your_webhook_base_url}/v2/webhooks/cards
The payload structure matches the Get Cards response item.
{
"id": "64120850-73a1-4df5-a074-d463258c9deb",
"card_wallet_address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874",
"status": "Active",
"status_reason": "",
"previous_status": "NotActivated",
"generation": "Gen2",
"provider": "Wirex",
"limit": {
"daily_limit": 10000.00,
"daily_usage": 0.00,
"currency": "USD"
},
"allowed_actions": [
{ "type": "Block", "relative_path": "/api/v1/cards/:cardId/block" },
{ "type": "Close", "relative_path": "/api/v1/cards/:cardId/close" }
],
"card_data": {
"card_name": "My Card",
"name_on_card": "Alex Grey",
"card_number_last_4": "0333",
"expiry_date": "12/2027",
"payment_system": "Visa",
"format": "Virtual"
},
"created_at": "2024-01-01T10:00:00Z",
"updated_at": "2024-01-01T10:05:00Z"
}Card Limit Webhook
Endpoint: POST {your_webhook_base_url}/v2/webhooks/card-limits
{
"card_id": "64120850-73a1-4df5-a074-d463258c9deb",
"transaction_limit": 500.00,
"daily_limit": 1000.00,
"daily_usage": 200.00,
"monthly_limit": 5000.00,
"monthly_usage": 1500.00,
"lifetime_limit": 100000.00,
"lifetime_usage": 25000.00
}Field Validation
Card Name
Pattern: ^[a-zA-Z0-9\-':+# @]{1,50}$
Name on Card
Pattern: ^[A-Za-z][A-Za-z .'-]{1,25}$
- Must start with a letter
- Only letters, spaces, periods, apostrophes, and hyphens
- 2-26 characters total
Delivery Address
| Field | Pattern |
|---|---|
line1 | ^[A-Za-z0-9&.,''\-/() :+#]{2,100}$ |
line2 | ^[A-Za-z0-9&.,''\-/() :+#]{2,100}$ |
city | ^[A-Za-z\s\-'.]{2,100}$ |
state | ^[A-Za-z][A-Za-z\s\-']{1,99}$ |
zip_code | ^[A-Za-z0-9\s\-]{3,12}$ |
country | ^[A-Z]{2}$ (ISO 3166-1 alpha-2) |
Error Handling
All errors return JSON with the following structure:
{
"error_reason": "ErrorInvalidField",
"error_description": "Human-readable message",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "field_name" }
]
}Validation Errors (400)
Missing Required Field
{
"error_reason": "ErrorMissingField",
"error_description": "delivery_address is required",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "delivery_address" },
{ "key": "issue", "details": "missing" }
]
}Invalid Field Format
{
"error_reason": "ErrorInvalidField",
"error_description": "Invalid format for card number last4",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "card_number_last4" },
{ "key": "issue", "details": "invalid_format" },
{ "key": "pattern", "details": "^\\d{4}$" }
]
}Invalid Card Status
Returned when attempting an action not allowed for the card's current status.
{
"error_reason": "ErrorInvalidStatus",
"error_description": "Card is not in valid status for this action",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "card_status" },
{ "key": "value", "details": "Requested" }
]
}| Action | Invalid Statuses |
|---|---|
| Activate | Requested, Active, Blocked, Closed |
| Block | NotActivated, Requested, Closed, Blocked |
| Unblock | Any status except Blocked |
| Close | NotActivated, Requested, Closed |
Delivery ID Required
When fees are enforced and delivery_id is missing or invalid.
{
"error_reason": "ErrorGeneral",
"error_description": "Delivery ID is required and must be positive",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "delivery_id" }
]
}Capability Not Active
{
"error_reason": "ErrorGeneral",
"error_description": "Virtual card capability is not active",
"error_category": {
"category": "CategoryInternalFailure",
"http_status_code": 500
}
}Not Found Errors (400)
Delivery Countries/Methods Not Found
{
"error_reason": "ErrorNotFound",
"error_description": "No delivery methods found for country",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "country" },
{ "key": "value", "details": "XX" }
]
}Fees Not Found
{
"error_reason": "ErrorNotFound",
"error_description": "No fees found for specified country and method",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "fields", "details": "type,country" },
{ "key": "type", "details": "Virtual" },
{ "key": "country", "details": "XX" }
]
}Fees Not Enforced
Returned when calling fees endpoints but fees are not configured for your company.
{
"error_reason": "ErrorGeneral",
"error_description": "Request validation failed",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "request", "details": "Card fees are not enforced" }
]
}Server Errors (500)
| Error Description | Cause |
|---|---|
| Failed to get card | Card not found or service unavailable |
| Failed to issue virtual card | Card issuer service error |
| Failed to issue plastic card | Card issuer service error |
| Failed to activate card | Card issuer service error |
| Failed to process delivery payment | Payment processing error |
Updated 8 days ago
