Test a card in sandbox

Issue, fund, freeze and change the limit of a sandbox card, and test the purchase, decline and withdrawal screens that sandbox cannot produce today.

Use this recipe to issue, fund, freeze and change the limit of a sandbox card, and to test the purchase, decline and withdrawal screens that sandbox cannot produce today.

Who it is for. An expense-card app that wants to show employees their card, its balance and its status changes before going live.

Status. Available for issuing, funding, reading and updating a virtual card. Not yet available in sandbox for purchases, declines, withdrawals from new cards and physical cards.

Money example

  1. Gravv issues Ada's sandbox card during activation, with a 100 USD daily limit.
  2. You fund it with 100 USD from a sandbox source on polygon. When the transfer is completed, the card's balance reads 100.
  3. You freeze it with {"status": "freeze", "limit": 100}. While the card is frozen, balance reads 0, and Gravv sends cards.status.update.
  4. You cannot make it spend 60 USD in sandbox today, so you test the purchase screen by posting a recorded cards.transaction.payment payload to your own endpoint.

Before you start

Find out what is required

Step In sandbox
Card application Approved at once
First card Issued during feature activation, with a daily limit equal to estimated_monthly_limit
Funding A transfer to the card from a source on polygon. The balance follows the card's wallet
A daily limit of 0 Means no limit in sandbox. In live, it declines every payment
Purchases and declines Not yet available
Withdrawal from a card issued since 14 September 2026 Not yet available
Physical cards Issuance fails with card creation failed!

Steps

1. Issue the card

Follow Issue a virtual card after a hosted check, with your sandbox key and your verified cardholder.

2. Fund it

Follow Top up a card. In sandbox, a funding transfer sends only transfer.status.pending events. Poll the transaction until it is completed, then read the balance.

curl --request GET \
 --url https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/balance \
 --header 'Api-Key: <API_KEY>'
const response = await fetch("https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/balance", {
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/a14ec356-f33d-4384-ba68-4d9bfa19765b/balance",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
},
)
print(response.status_code, response.json())

3. Freeze it and change its limit

Always send status and limit together.

curl --request PATCH \
 --url https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update \
 --header 'Api-Key: <API_KEY>' \
 --header 'Content-Type: application/json' \
 --data '
{
"status": "freeze",
"limit": 100
}
'
const response = await fetch("https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update", {
method: "PATCH",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "freeze",
"limit": 100
}),
});
console.log(response.status, await response.json());
import os

import requests

response = requests.patch(
"https://api.gravv.xyz/v1/cards/a14ec356-f33d-4384-ba68-4d9bfa19765b/update",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
    "Content-Type": "application/json",
},
json={
    "status": "freeze",
    "limit": 100,
},
)
print(response.status_code, response.json())

Unfreeze with {"status": "active", "limit": 100}. Gravv sends cards.status.update for each real status change.

4. Test the purchase and decline screens without a purchase

You cannot produce card purchases in sandbox today. To test your handler:

  • Post a recorded payload, such as the one in Card decline reasons, to your own endpoint. If your endpoint checks signatures, sign the payload yourself with your sandbox secret.
  • Or resend a stored event that your endpoint already received. Gravv does not sign a resend.

What can go wrong

Response Why Fix
400 card creation failed! You requested a physical card in sandbox Test virtual cards
400 this card withdraws by transfer: send a transfer from the card to its account You tried to withdraw from a new card Not yet available. Test the withdraw screen with a recorded payload
400 the balance for this account could not be confirmed — please try again shortly You sent a transfer from a new card to its card account Not yet available. Do not retry in a loop
The daily limit reads 0 after an update You sent the update without limit Send status and limit together
The card's balance does not move after a funding transfer The transfer is not completed yet Poll the transaction
400 card does not exist The id is a live card id, or belongs to another business's card Use a sandbox card id from GET /v1/cards

Webhooks you will receive

You receive cards.status.update for each real status change. You do not need cards.application.update in sandbox, because sandbox approves the application at once. You receive no purchase events, because sandbox cannot produce purchases.

Testing in sandbox

This page is the sandbox guide for cards. Sandbox also cannot produce refunds, reversals, settlement or foreign-currency purchases.

Reference

Was this page helpful?