Issue a business card
In sandbox, issue a card printed with a business customer's name to one of its associated persons.
Use this recipe in sandbox to issue a card printed with a business customer's name to one of the business's associated persons.
Who it is for. This recipe is for an expense platform whose client company wants its name printed on staff cards.
Status. Available in sandbox only. Not yet available in live. Contact support before you offer business cards.
Money example
The card shows the business name "Northwind Logistics".
- Jane, the finance lead, gets a card.
- You move 1,000 USD from the company account to the card.
- Jane pays a 240 USD software invoice. The card now holds 760 USD.
Before you start
KYB is KYC for a business, which checks the company and its people. See the Glossary.
| Prerequisite | How to check |
|---|---|
A business customer with a business_name |
GET /v1/customers/{customer_id} on the business shows type business |
| The business finished KYB | The business's basic KYC is completed |
| The cardholder is one of the business's associated persons and is verified | The person is in associated_persons[].id, and that person's basic KYC is completed |
| Your card account | See the hosted-check recipe |
| The person has no card application yet | GET /v1/cards?customer_id=… returns nothing |
Find out what is required
GET /v1/customers/{customer_id} on the business returns the business name, its KYB status and its associated persons. If one of them is missing, the application call names it.
Steps
1. Open the business card application
curl --request POST \
--url https://api.gravv.xyz/v1/cards/applications/new \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: 19781350-fe75-4ff1-a062-47c62eb6352f' \
--header 'Content-Type: application/json' \
--data '
{
"type": "business",
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"business_customer_id": "0d6c2f4e-6a1b-4f0e-9b8f-5c2a7d3e1f00",
"account_id": "9b40be8d-9d74-4d2a-8897-dea3ef80ad23",
"annual_remuneration": 60000,
"estimated_monthly_limit": 3000,
"ip_address": "203.0.113.24"
}
'
const response = await fetch("https://api.gravv.xyz/v1/cards/applications/new", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "19781350-fe75-4ff1-a062-47c62eb6352f",
"Content-Type": "application/json",
},
body: JSON.stringify({
"type": "business",
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"business_customer_id": "0d6c2f4e-6a1b-4f0e-9b8f-5c2a7d3e1f00",
"account_id": "9b40be8d-9d74-4d2a-8897-dea3ef80ad23",
"annual_remuneration": 60000,
"estimated_monthly_limit": 3000,
"ip_address": "203.0.113.24"
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.post(
"https://api.gravv.xyz/v1/cards/applications/new",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Idempotency-Key": "19781350-fe75-4ff1-a062-47c62eb6352f",
"Content-Type": "application/json",
},
json={
"type": "business",
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"business_customer_id": "0d6c2f4e-6a1b-4f0e-9b8f-5c2a7d3e1f00",
"account_id": "9b40be8d-9d74-4d2a-8897-dea3ef80ad23",
"annual_remuneration": 60000,
"estimated_monthly_limit": 3000,
"ip_address": "203.0.113.24",
},
)
print(response.status_code, response.json())
{
"data": {
"message": "Card application request successful",
"status": true,
"id": "b25fd467-a44e-4495-8b79-5e0cab2a876c"
},
"error": null
}
Set customer_id to the person and business_customer_id to the business. estimated_monthly_limit must not be above annual_remuneration. ip_address is the cardholder's IP address.
2. Check approval
List the applications with GET /v1/cards/applications. The item with this id shows application_status approved. Sandbox approves applications at once.
3. Issue the card
curl --request POST \
--url https://api.gravv.xyz/v1/cards \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: 368f9b5b-3958-4de3-87c7-ea03e27a5165' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"card_type": "virtual",
"card_limit": 500,
"name_on_card": "Jane Doe"
}
'
const response = await fetch("https://api.gravv.xyz/v1/cards", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "368f9b5b-3958-4de3-87c7-ea03e27a5165",
"Content-Type": "application/json",
},
body: JSON.stringify({
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"card_type": "virtual",
"card_limit": 500,
"name_on_card": "Jane Doe"
}),
});
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": "368f9b5b-3958-4de3-87c7-ea03e27a5165",
"Content-Type": "application/json",
},
json={
"customer_id": "9e8d7c6b-5a49-4382-a1b0-c9d8e7f6a5b4",
"card_type": "virtual",
"card_limit": 500,
"name_on_card": "Jane Doe",
},
)
print(response.status_code, response.json())
The first card's id is the application id. Next, top up the card.
What can go wrong
| Response (HTTP 400) | Why | Fix |
|---|---|---|
a business customer cannot hold a card; apply with type "business" for one of its associated persons |
customer_id is the business |
Send the person as customer_id and the business as business_customer_id |
an associated person's card must be a business card; apply with type "business" and their business_customer_id |
The request has no type |
Add "type": "business" |
business_customer_id is required when type is "business" |
business_customer_id is missing |
Send it |
the business must complete KYB before cards can be issued under it |
The business is not verified | Finish KYB |
the business has no business_name to print on its cards |
The business has no business_name |
Update the business |
customer_id is not an associated person of this business |
The person is not linked to the business | Add the person to the business |
basic level kyc needs to be completed to create an application |
The person is not verified. The API checks this first | Verify the person |
estimated_monthly_limit must be lesser than annual_renumeration |
estimated_monthly_limit is above annual_remuneration |
Lower estimated_monthly_limit |
200 with an application that has no message |
The person already has a card application, and the API returns it unchanged | Use a person with no application yet |
Webhooks you will receive
Gravv sends no webhook when the application opens in sandbox. For funding and card events, see Top up a card.
Testing in sandbox
This recipe runs in sandbox. Sandbox KYC does not complete today, so you need an associated person verified earlier and with no card application.