Create and Provision Flexible Credentials
Overview
This page explains how to create and provision a flexible credential and its member cards in a single call.
FlexibleCredentialStatus has exactly two values, ACTIVE and CLOSED. There is no observable state in which a credential exists but isn't yet provisioned.
The mutation provisionFlexibleCredential runs against an Application in the APPROVED state and completes synchronously, so there is no polling step.
A returned FlexibleCredential always represents a finished request.
Failures surface as UserError or AccessDeniedError in the payload union rather than as transport errors.
Before you start
You need the following:
-
An application in the
APPROVEDstate.The flexible credential's
CardProductand its owningAccountHolderare both derived from that application, so you do not pass either one. Provisioning against an application in any other state fails withAPPLICATION_NOT_APPROVED. -
A card profile set for each credential you intend to issue.
You need one card profile set for the primary, and one for the secondary. The card profile set does more than style the card; it determines the role each member card plays, and the role must match the backing account. A card profile set on a debit or prepaid BIN classifies as the primary, one on a credit BIN classifies as the secondary.
Highnote currently supports exactly one secondary credential, and intends to relax that limit. secondaryCards is a list so the request shape does not have to change when it does, but sending more than one fails validation today.
Describe the cards
The primaryCard field takes a single card slot and secondaryCards takes a list of them.
Every slot needs the same three fields:
| Field | Notes |
|---|---|
cardProfileSetId | The card profile set for this card. Also determines the card's role, and so which FinancialAccount backs it. |
expirationDate | Required on every slot, in ISO 8601 format. Flexible credential issuance has no product-level default to fall back on. |
activateOnCreate | Whether this card is usable immediately. A card created inactive must be activated separately before it can be used. |
Three behaviors are worth knowing before you build the request:
- Each slot's role must match its backing account. Highnote derives a role from the slot's
cardProfileSetIdand rejects the request if a slot's role does not match the account it would back. You do not choose the pairing directly; the card profile set determines it: debit and prepaid BINs classify as primary, credit BINs as secondary. - The
secondaryCardsarray is required and non-null. A credential issued with no secondary still has to send"secondaryCards": []. - The
activateOnCreate: falseflag only holds back the card. The backingFinancialAccountand the parent flexible credential are always materialized active. Setting it per slot lets you issue a live primary alongside an inactive secondary. This is useful when a secondary credential is enabled only after the cardholder opts in.
Provision the credential
The example below issues a live debit primary alongside a live revolving credit secondary.
ProvisionFlexibleCredential
Query
mutation ProvisionFlexibleCredential(
$input: ProvisionFlexibleCredentialInput!
) {
provisionFlexibleCredential(input: $input) {
... on FlexibleCredential {
id
name
externalId
status
createdAt
cards {
isPrimary
paymentCard {
id
last4
expirationDate
}
defaultFinancialAccount {
id
name
}
}
}
... on UserError {
errors {
code
description
errorPath
}
}
... on AccessDeniedError {
message
}
}
}
Variables
{ "input": { "applicationId": "APPLICATION_ID", "name": "Cardholder Flexible Credential", "idempotencyKey": "IDEMPOTENCY_KEY", "primaryCard": { "cardProfileSetId": "CARD_PROFILE_SET_ID_DEBIT", "expirationDate": "2030-01-01T00:00:00Z", "activateOnCreate": true }, "secondaryCards": [ { "cardProfileSetId": "CARD_PROFILE_SET_ID_CREDIT", "expirationDate": "2030-06-01T00:00:00Z", "activateOnCreate": true } ] } }
Result
{
"data": {
"provisionFlexibleCredential": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"name": "Cardholder Flexible Credential",
"externalId": "EXTERNAL_ID",
"status": "ACTIVE",
"createdAt": "2026-04-01T15:30:00.000Z",
"cards": [
{
"isPrimary": true,
"paymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242",
"expirationDate": "2030-01-01T00:00:00.000Z"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account"
}
},
{
"isPrimary": false,
"paymentCard": {
"id": "PAYMENT_CARD_ID_SECONDARY",
"last4": "1881",
"expirationDate": "2030-06-01T00:00:00.000Z"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_CREDIT",
"name": "Revolving Credit Account"
}
}
]
}
}
}
The mutation returns the new FlexibleCredential, so you can select the card and account IDs in the same request instead of following up with a read.
Name and external ID
The name field is required and holds a human-friendly label for the credential.
The externalId field is optional and is yours to supply for your own reconciliation.
It must be unique per organization and tenant, and Highnote generates one if you omit it.
Send an idempotency key
Send a UUIDv4 idempotencyKey with every request.
Highnote processes only the first request carrying a given key and returns that original response to repeats for 24 hours.
A retry after a timeout cannot issue a second set of cards.
Handle failures
Provisioning failures come back as UserError in the payload union rather than as a transport error, so check __typename before reading the credential.
| Code | Meaning |
|---|---|
APPLICATION_NOT_APPROVED | The Application is not APPROVED. Provisioning cannot proceed until it is. |
INVALID_INPUT | A field failed validation, e.g., an expirationDate that is not ISO 8601. |
The errorPath locates the offending field, including its position in a list, so a bad expiration on the primary member card returns ["input", "primaryCard", "expirationDate"]. Read it rather than parsing the description.
Next steps
The credential is active and its cards are issued. From here, find it and fetch what's attached to it using the returned ID.