Issue a second or physical card
Give a cardholder up to 10 cards, sharing their balance or with a balance of their own. Physical cards are Not yet available.
Use this recipe to give a cardholder up to 10 cards. Each extra card shares the cardholder's balance or has a balance of its own. Physical cards are Not yet available.
Who it is for. This recipe is for a consumer app that gives Ada a separate card for subscriptions.
Status. Additional virtual cards are Available. Physical cards are Not yet available.
Money example
Ada's main card holds 131.25 USD.
- You issue her a "subscriptions" virtual card with a balance of its own and a 50 USD daily limit.
- You move 30 USD to the new card. The main card still holds 131.25 USD, and the subscriptions card holds 30 USD.
- A 9.99 USD subscription charges the subscriptions card, which leaves 20.01 USD on it.
Before you start
- Ada's application is approved and her first card exists. Check with
GET /v1/cards?customer_id=…. - She holds fewer than 10 cards that are not blocked.
Find out what is required
Count Ada's cards:
curl --request GET \
--url 'https://api.gravv.xyz/v1/cards?customer_id=5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42&status=active,freeze,inactive' \
--header 'Api-Key: <API_KEY>'
const response = await fetch("https://api.gravv.xyz/v1/cards?customer_id=5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42&status=active,freeze,inactive", {
method: "GET",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
},
});
console.log(response.status, await response.json());
import os
import requests
response = requests.get(
"https://api.gravv.xyz/v1/cards?customer_id=5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42&status=active,freeze,inactive",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
},
)
print(response.status_code, response.json())
Steps
1. Issue a second virtual card with its own balance
Send a new Idempotency-Key for each extra card.
curl --request POST \
--url https://api.gravv.xyz/v1/cards \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: 3211a0fd-795d-4218-bc6e-4966abe161ab' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
"card_type": "virtual",
"card_limit": 50,
"name_on_card": "Ada Obi",
"funding": {
"mode": "own"
}
}
'
const response = await fetch("https://api.gravv.xyz/v1/cards", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "3211a0fd-795d-4218-bc6e-4966abe161ab",
"Content-Type": "application/json",
},
body: JSON.stringify({
"customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
"card_type": "virtual",
"card_limit": 50,
"name_on_card": "Ada Obi",
"funding": {
"mode": "own"
}
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.post(
"https://api.gravv.xyz/v1/cards",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Idempotency-Key": "3211a0fd-795d-4218-bc6e-4966abe161ab",
"Content-Type": "application/json",
},
json={
"customer_id": "5b0f6c1e-2a8f-4c39-9d0e-0c7e3b9a1f42",
"card_type": "virtual",
"card_limit": 50,
"name_on_card": "Ada Obi",
"funding": {
"mode": "own",
},
},
)
print(response.status_code, response.json())
{
"data": {
"message": "Card creation request is processing",
"status": true,
"id": "0c9b8a7f-6e5d-4c4b-9a3f-2e1d0c9b8a76",
"fee_charged": false
},
"error": null
}
Send {"mode": "shared"}, or leave funding out, to spend from the same balance as the main card.
2. Check it
GET /v1/cards/0c9b8a7f-6e5d-4c4b-9a3f-2e1d0c9b8a76 shows card_type virtual, status active and the 50 USD limit. Next, top it up.
Physical cards
Not yet available. A physical card cannot make a payment today. In sandbox, the request returns card creation failed! and leaves an empty card row. In live, the card starts inactive, and no API call activates it. For the shipping address rules, see Physical card shipping.
What can go wrong
| Response (HTTP 400 unless noted) | Why | Fix |
|---|---|---|
customer already holds the maximum of 10 cards |
The cardholder has reached the cap of 10 cards | Block an unused card |
missing idempotency key in request headers |
The request has no Idempotency-Key |
Add one |
422 idempotency_key_reused |
You sent the same key with another body | Use a new key for each card |
funding.mode must be "shared" or "own" |
The mode is invalid | Send shared or own |
this card's balance is a new provider account whose application is <status>; retry with the same Idempotency-Key once it is approved |
In live, an own balance waits for approval |
See Issue expense cards to employees |
card creation failed! |
The card program refused the card. Every physical card request in sandbox returns this today | Issue a virtual card, and ignore the empty row |
Webhooks you will receive
Gravv sends no webhook when you issue a card. You receive cards.transaction.* events once the cardholder uses the card.
Testing in sandbox
Additional virtual cards work in sandbox. A card with funding.mode own gets its own card_group_id. Physical cards fail to issue.