Virtual cards

Spend & Expense API cards

In this section, we walk through understanding cards in BILL Spend & Expense.

❗️

Use uuid in the Spend & Expense API

In February 2025, BILL introduced UUIDs in the Spend & Expense API for further improving its security standards. All API responses include both id and uuid values.

As of July 9, 2025, the Spend & Expense id values are being deprecated. Both id and uuid values will continue to be available in all API responses. We strongly recommend that you use uuid instead of id in all your Spend & Expense API operations.

What is a Spend & Expense card?

A Spend & Expense card is a unique 16-digit card number that is generated and linked to your Spend & Expense account. Cards can be physical or virtual. In the next set of sections, we will create virtual cards.

You can create cards and set limits based on different needs, such as budgets, users, and expiration date. In addition, you can create vendor cards and associate these cards with specific vendors.

Use POST /v3/spend/cards to create a card. You can then use GET /v3/spend/cards/{cardUuid}/pan-jwt to get a JWT token for the card Primary Account Number (PAN). The JWT token contains all the information required for retrieving details about a card (16-digit PAN, CVV, and expiration date). The JWT token lifespan is 5 minutes.

See the /v3/spend/cards API for the complete list of available operations.

Create a virtual card with a budget limit

In this section, we create a virtual card and assign it to an existing budget and existing user. See Budgets and Users to learn about creating budgets and users.

In your POST /v3/spend/cards request, set the required fields.

FieldDescription
nameCard name
userUuidBILL-generated UUID of the user
budgetUuidBILL-generated UUID of the budget
limitCurrent budget spend limit assigned to the card

See the POST /v3/spend/cards API for more information about the other card fields you can set.

Sample request

In this cURL example, a virtual card is created with the specified details. The userUuid and budgetUuid values are set to assign the card to the budget and user. A spend limit of $10 is assigned to the card.

curl --request POST \
--url 'https://gateway.stage.bill.com/connect/v3/spend/cards' \
--header 'apiToken: {api_token}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "budgetId": "{budget_uuid}",
  "limit": "10",
  "name": "Happy Music Supplies card for Clark Spenderson",
  "userId": "\{user_uuid}"
}'

Response

In the response, a BILL-generated card uuid is available. The currentPeriod object provides information about the current budget assigned to the card.

👍

All cards in sandbox are frozen

In the sandbox environment, the status for all created cards is FROZEN.

{
    "uuid": "{card_uuid}",
    "id": "{card_id}",
    "name": "Happy Music Supplies card for Clark Spenderson",
    "userUuid": "\{user_uuid}",
    "userId": "\{user_id}",
    "budgetUuid": "{budget_uuid}",
    "budgetId": "{budget_id}",
    "lastFour": "4242",
    "validThru": "12/25",
    "status": "FROZEN",
    "type": "VIRTUAL_VENDOR",
    "shareBudgetFunds": false,
    "recurring": false,
    "currentPeriod": {
        "limit": 10.00,
        "spent": 0.00
    },
    "createdTime": "2025-12-16T20:24:35.000+00:00",
    "updatedTime": "2025-12-16T20:24:35.000+00:00"
}

Create a virtual card with custom budget limits

In addition to virtual cards with budget controls, you can create virtual cards with a custom limit. This is useful when you want to create virtual cards and do not have any budget controls.

Create a budget & enable spending over budget limits

In your POST /v3/spend/budgets request, set the required fields.

FieldDescription
nameBudget name
ownersIn the owners array, set the user UUID of each budget owner. You can get the UUID of the current user with GET /v3/spend/users/current.
recurringIntervalInterval after which the budget is reset. You can set this value as NONE.
limitlessOverspendSet as true for enabling spend amounts over the budget limits

In this cURL example, a budget is created with the specified details. Policy rules and an owner is set for the budget. The budget owner is set as the ID of the current user. The limitlessOverspend field is set as true to enable spend amounts over any budget limits. This creates a budget without any budget controls.

curl --request POST \
--url 'https://gateway.stage.bill.com/connect/v3/spend/budgets' \
--header 'apiToken: {se_api_token}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "name": "Happy Music Supplies unlimited budget",
  "owners": [
    "{current_user_uuid}"
  ]
  "recurringInterval": "NONE",
  "limitlessOverspend": true
}'

Create a virtual card for the budget without limits

Create a virtual card for this budget without limits. In your POST /v3/spend/cards request, set the required fields.

FieldDescription
nameCard name
userUuidBILL-generated UUID of the user
budgetUuidBILL-generated UUID of the budget
shareBudgetFundsSet as true to share all the budget funds with the card

In this cURL example, a virtual card is created with the specified details. The userUuid and budgetUuid values are set to assign the card to the budget and user. The shareBudgetFunds field is set as true to share all the budget funds with the card.

curl --request POST \
--url 'https://gateway.stage.bill.com/connect/v3/spend/cards' \
--header 'apiToken: {api_token}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "budgetId": "{budget_uuid}",
  "name": "Happy Music Supplies card for Clark Spenderson",
  "userId": "\{user_uuid}",
  "shareBudgetFunds": true
}'

Card management

When you create a card, you can control the use of the card with two endpoints.

Endpoint

Description

Freeze a card with POST /v3/spend/cards/{cardUuid}/freeze

Freeze an existing card. You can freeze virtual cards and physical cards. Frozen cards cannot be used for payments.

In the response, the card status is set as FROZEN.

Unfreeze a card with POST /v3/spend/cards/{cardUuid}/unfreeze

Unfreeze an existing card.

In the response, the card status returns to what it was before the card was frozen. For example, if the card status was ACTIVATED before it was frozen, unfreezing the card restores the card status as ACTIVATED.

Get a PAN JWT token

The JWT token for a card contains all the information required for retrieving details about the card (16-digit PAN, CVV, and expiration date). The JWT token lifespan is 5 minutes.

In your GET /v3/spend/cards/{cardUuid}/pan-jwt request, set the required fields.

FieldDescription
cardUuidBILL-generated UUID of the card

Sample request

In this cURL example, a JWT token is requested for the specified card uuid.

curl --request POST \
--url 'https://gateway.stage.bill.com/connect/v3/spend/cards/{card_uuid}/pan-jwt' \
--header 'apiToken: {api_token}' \
--header 'Accept: application/json'

Response

In the response, a JWT token is available for the specified card.

JWT to PAN details

You can use the JWT token from the GET /v3/spend/cards/{cardUuid}/pan-jwt response to get PAN details about the card.

curl --request POST \
--url 'https://app-dev-bdc-stg.divvy.co/de/rest/pan' \
--header 'Content-Type: application/json' \
--data-raw '{"token":"{JWT_token}"}'
curl --request POST \
--url 'https://api.divvy.co/de/rest/pan' \
--header 'Content-Type: application/json' \
--data-raw '{"token":"{JWT_token}"}'

Response

In the response, the PAN details about the card are available.

{
    "expirationDate": "202912",
    "cvv": "{cvv_number}",
    "cardNumber": "{card_number}"
}

See the /v3/spend/cards API for the complete list of available operations.