Create Virtual Card for Auth User
Create a virtual card on behalf of an authorized user on the caller's account. Pass the authorized user's authUserId (the UAC role assignment ID returned by addAuthorizedUser) to create the card for that user's underlying cardholder record while keeping the card scoped to the caller's account.
The authUserId must belong to the caller's account, must be ACTIVE, and cannot be an OWNER assignment. If addAuthorizedUser returns PENDING, the authorized user must accept the invite before this mutation can use that authUserId.
Virtual card creation can use a saved billing address (userAddressId) or an inline billingAddress. For Highnote-backed offers, providing either address value triggers billing-address registration with the card issuer before the card is issued. If issuer approval is still pending after the server-side wait window, the mutation returns GraphQL error code VC-0020 with extensions.addressId; retry with that value as userAddressId.
🔒 Restricted Access
This mutation requires a Bearer token with the CREATE_VIRTUALCARD scope.
mutation CreateVirtualCard($input: CreateVirtualCardInput!) {
createVirtualCard(input: $input) {
virtualCardId
userId
cardholderName
expiryMonth
expiryYear
virtualCardLast4
status
cardType
initialAmount
usedAmount
createdAt
authorizationSetting {
lockDate
dailySpendLimit
weeklySpendLimit
monthlySpendLimit
}
}
}Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| input | CreateVirtualCardInput! | Yes | Wrapper object for the virtual card creation request. |
| input.idempotencyKey | UUID! | Yes | Unique client-generated UUID. The same key prevents the same request from being processed more than once. |
| input.spendLimit | Float! | Yes | Maximum amount that can be charged to the card. Minimum is $5; at most two decimal places are allowed. |
| input.offerId | UUID | Yes | Virtual card offer ID. Use getVirtualCardOffers to fetch active offers. |
| input.authUserId | UUID | No | Authorized user ID (UAC role assignment ID) to create the card on behalf of. Must be ACTIVE and non-owner. |
| input.userAddressId | UUID | No | Saved billing address ID for the caller or authorized cardholder. Takes precedence over billingAddress. |
| input.billingAddress | VirtualCardBillingAddressInput | No | New billing address to save and register with the card program before issuing the card. |
| input.spendLimitDuration | VirtualCardSpendLimitDuration | No | Card limit duration. Defaults to LIFETIME. |
| input.lockDate | String | No | Future lock date. Defaults to 47 months from the request date. |
| input.lockCardNextUse | Boolean | No | Whether to lock the card after the next use. Defaults to false. |
| input.cardNickname | String | No | Card nickname. Must be 1-50 characters when provided. |
| input.primaryFundingSource | VirtualCardFundingSource | No | Primary funding source. Defaults to FLUZ_BALANCE. If BANK_ACCOUNT, bankAccountId is required. |
| input.bankAccountId | UUID | No | Bank account ID used when primaryFundingSource is BANK_ACCOUNT. |
| input.userCashBalanceId | UUID | No | Spend account ID to attach to the card. |
| input.usePrepaymentBalance | Boolean | No | When false, the card will not draw from prepaid balance. Defaults to true. |
| input.useRewardsBalance | Boolean | No | When false, the card will not draw from rewards balance. Defaults to true. |
| input.memo | String | No | Optional note attached to the transaction. Maximum 255 characters. |
| input.transactionCategory | String | No | Optional category name. A matching category is found or created for the account. Maximum 100 characters. |
Response
Success Response
{
"data": {
"createVirtualCard": {
"virtualCardId": "6d9f0d0f-21f0-4ad1-8fc7-2fb7dd58ce11",
"userId": "f1320ac4-52dc-4c67-9e80-24e506b18450",
"cardholderName": "Ada Lovelace",
"expiryMonth": "08",
"expiryYear": "2029",
"virtualCardLast4": "4242",
"status": "ACTIVE",
"cardType": "MULTI_USE",
"initialAmount": 100,
"usedAmount": 0,
"createdAt": "2026-05-15T15:18:00.000Z",
"authorizationSetting": {
"lockDate": "2029-08-15",
"dailySpendLimit": null,
"weeklySpendLimit": null,
"monthlySpendLimit": null
}
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
virtualCardId | UUID | Created virtual card ID. |
userId | UUID | User ID of the cardholder. When authUserId is provided, this is the authorized user's user ID. |
cardholderName | String | Cardholder name shown on the virtual card. |
expiryMonth | String | Virtual card expiration month. |
expiryYear | String | Virtual card expiration year. |
virtualCardLast4 | String | Last 4 digits of the virtual card number. |
status | String | Virtual card status, such as ACTIVE. |
cardType | String | Virtual card type. |
initialAmount | Float | Initial spend limit requested for the card. |
usedAmount | Float | Amount already spent on the card. |
createdAt | DateTime | Time when the card was created. |
authorizationSetting | Object | Authorization settings attached to the virtual card. |
Example Requests
Create a card for an authorized user with a saved address:
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_access_token>" \
-d '{
"query": "mutation CreateVirtualCard($input: CreateVirtualCardInput!) { createVirtualCard(input: $input) { virtualCardId userId cardholderName virtualCardLast4 status cardType initialAmount usedAmount createdAt } }",
"variables": {
"input": {
"idempotencyKey": "931e8841-cf23-4f36-8bf8-169384042ec2",
"offerId": "09a9c8d1-9c4b-46fa-8a7a-508812a2a0d9",
"authUserId": "8b2c1e0a-7d4f-4a9b-9c2d-1f3e4a5b6c7d",
"userAddressId": "1f6b2c3d-4e5f-4a67-9a10-2b3c4d5e6f70",
"spendLimit": 100,
"spendLimitDuration": "LIFETIME",
"cardNickname": "Ada Travel Card",
"primaryFundingSource": "FLUZ_BALANCE"
}
}
}'Create a card for an authorized user with a new inline billing address:
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_access_token>" \
-d '{
"query": "mutation CreateVirtualCard($input: CreateVirtualCardInput!) { createVirtualCard(input: $input) { virtualCardId userId cardholderName virtualCardLast4 status cardType initialAmount usedAmount createdAt } }",
"variables": {
"input": {
"idempotencyKey": "c20341f0-47e3-4d52-8361-1d7c37736b65",
"offerId": "09a9c8d1-9c4b-46fa-8a7a-508812a2a0d9",
"authUserId": "8b2c1e0a-7d4f-4a9b-9c2d-1f3e4a5b6c7d",
"spendLimit": 100,
"billingAddress": {
"streetAddressLine1": "456 Market St",
"streetAddressLine2": "Suite 200",
"country": "United States",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105"
}
}
}
}'const response = await fetch('https://transactional-graph.staging.fluzapp.com/api/v1/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
query: `
mutation CreateVirtualCard(
$input: CreateVirtualCardInput!
) {
createVirtualCard(input: $input) {
virtualCardId
userId
cardholderName
virtualCardLast4
status
cardType
initialAmount
usedAmount
createdAt
}
}
`,
variables: {
input: {
idempotencyKey: '931e8841-cf23-4f36-8bf8-169384042ec2',
offerId: '09a9c8d1-9c4b-46fa-8a7a-508812a2a0d9',
authUserId: '8b2c1e0a-7d4f-4a9b-9c2d-1f3e4a5b6c7d',
userAddressId: '1f6b2c3d-4e5f-4a67-9a10-2b3c4d5e6f70',
spendLimit: 100,
spendLimitDuration: 'LIFETIME',
cardNickname: 'Ada Travel Card',
primaryFundingSource: 'FLUZ_BALANCE',
},
},
}),
});
const data = await response.json();
if (data.errors?.[0]?.extensions?.code === 'VC-0020') {
const userAddressId = data.errors[0].extensions.addressId;
console.log('Billing address pending issuer approval. Retry with userAddressId:', userAddressId);
} else {
console.log('Virtual card created:', data.data.createVirtualCard);
}Full Flow: Register User, Add Authorized User, Add Address, Create Card
This flow registers a new Fluz user, adds that user to the caller's account as an authorized user, saves a billing address for that authorized cardholder, then creates a virtual card on their behalf.
Step 1: Register the user. This requires a Bearer token for the developer user of an ACTIVE application with registration enabled.
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <developer_access_token>" \
-d '{
"query": "mutation RegisterUser($firstName: String!, $lastName: String!, $phoneNumber: String!, $regionCode: String!, $emailAddress: String!, $dateOfBirth: String!) { registerUser(firstName: $firstName, lastName: $lastName, phoneNumber: $phoneNumber, regionCode: $regionCode, emailAddress: $emailAddress, dateOfBirth: $dateOfBirth) { success error { code message } } }",
"variables": {
"firstName": "Ada",
"lastName": "Lovelace",
"phoneNumber": "5555555555",
"regionCode": "US",
"emailAddress": "[email protected]",
"dateOfBirth": "1990-01-31"
}
}'Step 2: Add the registered user as an authorized user on the caller's account.
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <account_owner_access_token>" \
-d '{
"query": "mutation AddAuthorizedUser($email: String, $roles: [UACRoleType!]!) { addAuthorizedUser(email: $email, roles: $roles) { success authUserId roles status pendingActionId error { code message } } }",
"variables": {
"email": "[email protected]",
"roles": ["SPENDER"]
}
}'Continue only after the returned status is ACTIVE. If the status is PENDING, the authorized user must accept the invite before addVirtualCardAddress or createVirtualCard can use the returned authUserId.
Step 3: Save the authorized user's billing address.
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <account_owner_access_token>" \
-d '{
"query": "mutation AddVirtualCardAddress($input: AddVirtualCardAddressInput!) { addVirtualCardAddress(input: $input) { userAddressId streetAddressLine1 streetAddressLine2 country city state postalCode } }",
"variables": {
"input": {
"authUserId": "8b2c1e0a-7d4f-4a9b-9c2d-1f3e4a5b6c7d",
"billingAddress": {
"streetAddressLine1": "456 Market St",
"streetAddressLine2": "Suite 200",
"country": "United States",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105"
}
}
}
}'Step 4: Create the virtual card for the authorized user with the saved address.
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <account_owner_access_token>" \
-d '{
"query": "mutation CreateVirtualCard($input: CreateVirtualCardInput!) { createVirtualCard(input: $input) { virtualCardId userId cardholderName virtualCardLast4 status cardType initialAmount usedAmount createdAt } }",
"variables": {
"input": {
"idempotencyKey": "931e8841-cf23-4f36-8bf8-169384042ec2",
"offerId": "09a9c8d1-9c4b-46fa-8a7a-508812a2a0d9",
"authUserId": "8b2c1e0a-7d4f-4a9b-9c2d-1f3e4a5b6c7d",
"userAddressId": "1f6b2c3d-4e5f-4a67-9a10-2b3c4d5e6f70",
"spendLimit": 100,
"spendLimitDuration": "LIFETIME",
"cardNickname": "Ada Travel Card",
"primaryFundingSource": "FLUZ_BALANCE"
}
}
}'Error Codes
| Code | Message | Description |
|---|---|---|
ARG-0001 | Invalid arguments received | Required input is missing or invalid, spendLimit is below $5, lockDate is not in the future, offerId is invalid, authUserId is invalid, userAddressId does not belong to the account/cardholder, or BANK_ACCOUNT funding was selected without bankAccountId. |
AUTH-0008 | Invalid user access | The Bearer token could not be resolved to a caller. Verify the token is valid. |
AUTH-0031 | The requested scopes must be granted by the user first. | The token is missing the CREATE_VIRTUALCARD scope required to create virtual cards. |
AUTH-0034 | No authorized user found with this id on the caller's account. | The authUserId does not exist on the caller's account, is not ACTIVE, or refers to an OWNER assignment. |
VC-0001 | Please try another payment method. If you continue experiencing issues, please contact our support team. | The card could not be created or the issuer rejected the billing address. |
VC-0019 | We are unable to get the virtual card offer. If you continue experiencing issues, please contact our support team. | The requested virtual card offer could not be resolved. |
VC-0020 | Virtual card billing address is pending approval. Please retry shortly using the returned addressId. | The billing address was submitted to the issuer but was not approved before the server-side wait window ended. Retry using extensions.addressId as userAddressId. |
Updated 6 days ago
