Find and Fetch Flexible Credentials
Overview
This page explains how to read a flexible credential and everything attached to it, including member cards, activity, and the events that record which credentials a transaction tried.
There is no root query for FlexibleCredential, so every read starts from an ID you already have.
First find the credential from its own ID, a PaymentCard, a FinancialAccount, or an account holder.
Then fetch the activity attached to it.
Transactions carry their own record of which credentials were tried, so that read starts from the transaction instead.
Resolve the credential through node(id: ID!): Node with an inline fragment, or follow the reverse pointer from a card or account.
For the object model, see How it's structured.
The connections on this page paginate on different objects, which changes how cursors behave. See Pagination.
Find a credential
Each starting point reaches the credential through a different field.
PaymentCard and FinancialAccount each carry a flexibleCredential reverse pointer, which resolves to null when that card or account is not part of one.
Refer to the examples just below.
| Starting from ID | Use node(id) on | Resulting field |
|---|---|---|
FlexibleCredential ID | FlexibleCredential | FlexibleCredential |
PaymentCard ID | PaymentCard | FlexibleCredential or null |
FinancialAccount ID | FinancialAccount | FlexibleCredential or null |
USPersonAccountHolder, USBusinessAccountHolder, Business, or Organization ID | USPersonAccountHolder | FlexibleCredentialConnection |
From a Flexible Credential ID
query FlexibleCredentialById($id: ID!) {
node(id: $id) {
... on FlexibleCredential {
id
status
}
}
}
Full query and response: Find a credential by ID.
From a Payment Card ID
query FlexibleCredentialFromPaymentCard($id: ID!) {
node(id: $id) {
... on PaymentCard {
flexibleCredential {
id
}
}
}
}
Returns null when the card is not part of a flexible credential.
Full query and response: Find the credential for a card.
From a Financial Account ID
query FlexibleCredentialFromFinancialAccount($id: ID!) {
node(id: $id) {
... on FinancialAccount {
flexibleCredential {
id
}
}
}
}
Returns null when the account does not back a member card.
Full query and response: Find the credential for a financial account.
From an account holder ID
query FlexibleCredentialsForAccountHolder($id: ID!) {
node(id: $id) {
... on USPersonAccountHolder {
flexibleCredentials(first: 20) {
edges {
node {
id
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
Full query and response: Find credentials for an account holder.
Find a credential by ID
You can fetch a FlexibleCredential and unpack every member card on it, including the primary.
FlexibleCredentialByNode
Query
query FlexibleCredentialByNode($id: ID!) {
node(id: $id) {
... on FlexibleCredential {
id
externalId
name
status
createdAt
updatedAt
primaryPaymentCard {
id
last4
}
cards {
isPrimary
paymentCard {
id
last4
}
defaultFinancialAccount {
id
name
}
}
accountHolder {
... on USPersonAccountHolder {
id
}
... on USBusinessAccountHolder {
id
}
... on Business {
id
}
... on Organization {
id
}
}
cardProduct {
id
name
}
}
}
}
Variables
{ "id": "FLEXIBLE_CREDENTIAL_ID" }
Result
{
"data": {
"node": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"externalId": "EXTERNAL_ID",
"name": "Cardholder Flexible Credential",
"status": "ACTIVE",
"createdAt": "2026-04-01T15:30:00.000Z",
"updatedAt": "2026-04-01T15:30:00.000Z",
"primaryPaymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
},
"cards": [
{
"isPrimary": true,
"paymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account"
}
},
{
"isPrimary": false,
"paymentCard": {
"id": "PAYMENT_CARD_ID_SECONDARY",
"last4": "1881"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_CREDIT",
"name": "Revolving Credit Account"
}
}
],
"accountHolder": {
"id": "ACCOUNT_HOLDER_ID"
},
"cardProduct": {
"id": "CARD_PRODUCT_ID",
"name": "Flexible Credential Card Product"
}
}
}
}
Find the credential for a card
Given a PaymentCard ID, you can resolve its parent FlexibleCredential and list every sibling card on the credential.
The result is null when the card is not part of a flexible credential.
FlexibleCredentialFromPaymentCard
Query
query FlexibleCredentialFromPaymentCard($id: ID!) {
node(id: $id) {
... on PaymentCard {
id
last4
flexibleCredential {
id
status
cards {
isPrimary
paymentCard {
id
last4
}
defaultFinancialAccount {
id
name
}
}
}
}
}
}
Variables
{ "id": "PAYMENT_CARD_ID_PRIMARY" }
Result
{
"data": {
"node": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242",
"flexibleCredential": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"status": "ACTIVE",
"cards": [
{
"isPrimary": true,
"paymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account"
}
},
{
"isPrimary": false,
"paymentCard": {
"id": "PAYMENT_CARD_ID_SECONDARY",
"last4": "1881"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_CREDIT",
"name": "Revolving Credit Account"
}
}
]
}
}
}
}
Find the credential for a financial account
Given a FinancialAccount ID, you can resolve the FlexibleCredential whose member card it backs, along with every card on that credential.
The result is null when the account does not back a member card.
FlexibleCredentialFromFinancialAccount
Query
query FlexibleCredentialFromFinancialAccount($id: ID!) {
node(id: $id) {
... on FinancialAccount {
id
name
flexibleCredential {
id
status
cards {
isPrimary
paymentCard {
id
last4
}
defaultFinancialAccount {
id
name
}
}
}
}
}
}
Variables
{ "id": "FINANCIAL_ACCOUNT_ID_DEBIT" }
Result
{
"data": {
"node": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account",
"flexibleCredential": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"status": "ACTIVE",
"cards": [
{
"isPrimary": true,
"paymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account"
}
},
{
"isPrimary": false,
"paymentCard": {
"id": "PAYMENT_CARD_ID_SECONDARY",
"last4": "1881"
},
"defaultFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_CREDIT",
"name": "Revolving Credit Account"
}
}
]
}
}
}
}
Find credentials for an account holder
To find credentials for any account holder, the flexibleCredentials connection is exposed on Business, Organization, USBusinessAccountHolder, and USPersonAccountHolder.
The page size is bounded by Highnote's standard pagination limit (1 to 20 items per page).
FlexibleCredentialsForAccountHolder
Query
query FlexibleCredentialsForAccountHolder(
$id: ID!
$first: Int! = 20
$after: String
) {
node(id: $id) {
... on USPersonAccountHolder {
id
flexibleCredentials(first: $first, after: $after) {
edges {
cursor
node {
id
name
status
primaryPaymentCard {
id
last4
}
}
}
pageInfo {
hasNextPage
endCursor
startCursor
}
}
}
}
}
Variables
{ "id": "US_PERSON_ACCOUNT_HOLDER_ID", "first": 20 }
Result
{
"data": {
"node": {
"id": "US_PERSON_ACCOUNT_HOLDER_ID",
"flexibleCredentials": {
"edges": [
{
"cursor": "CURSOR_1",
"node": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"name": "Cardholder Flexible Credential",
"status": "ACTIVE",
"primaryPaymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
}
}
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "CURSOR_1",
"startCursor": "CURSOR_1"
}
}
}
}
}
Fetch what the credential holds
Once you have the credential, you can retrieve the activity of its member accounts.
Fetch activity across credentials
flexibleCredentialActivities answers what is happening across the credential now.
It returns the activity of every member financial account as a single, chronologically ordered feed, so you do not have to fetch each credential's account separately and interleave the results yourself.
Each row carries sourceFinancialAccount, which attributes it to the credential it came from.
FlexibleCredentialActivities
Query
query FlexibleCredentialActivities(
$id: ID!
$first: Int! = 20
$after: String
) {
node(id: $id) {
... on FlexibleCredential {
id
flexibleCredentialActivities(first: $first, after: $after) {
edges {
cursor
node {
sourceFinancialAccount {
id
name
}
activity {
createdAt
sign
isComplete
pendingAmount {
value
currencyCode
}
postedAmount {
value
currencyCode
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
Variables
{ "id": "FLEXIBLE_CREDENTIAL_ID", "first": 20 }
Result
{
"data": {
"node": {
"id": "FLEXIBLE_CREDENTIAL_ID",
"flexibleCredentialActivities": {
"edges": [
{
"cursor": "CURSOR_1",
"node": {
"sourceFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_CREDIT",
"name": "Revolving Credit Account"
},
"activity": {
"createdAt": "2026-04-01T15:30:00.000Z",
"sign": "NEGATIVE",
"isComplete": false,
"pendingAmount": {
"value": 3500,
"currencyCode": "USD"
},
"postedAmount": null
}
}
},
{
"cursor": "CURSOR_2",
"node": {
"sourceFinancialAccount": {
"id": "FINANCIAL_ACCOUNT_ID_DEBIT",
"name": "Debit Account"
},
"activity": {
"createdAt": "2026-03-30T09:12:00.000Z",
"sign": "NEGATIVE",
"isComplete": true,
"pendingAmount": null,
"postedAmount": {
"value": 1250,
"currencyCode": "USD"
}
}
}
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "CURSOR_2"
}
}
}
}
}
This connection is aggregated at the flexible credential, so its cursors span the whole feed and after pages through all credentials together.
Trace a transaction's routing
This read starts from a transaction rather than a credential.
Find attempt events on a transaction
Highnote records a FlexibleCredentialAttemptEvent on the transaction for each credential that is tried and declines.
Every transaction also carries exactly one AuthorizationEvent, which holds the final outcome on the last credential tried, whether that credential approved or declined.
This is how you reconstruct, after the fact, which credentials were tried and how each one responded.
A credential that is never tried produces no attempt event. Transactions restricted to the primary credential drop the secondaries before selection runs, and a card verification failure skips selection entirely.
Filter a transaction's events with FLEXIBLE_CREDENTIAL_ATTEMPT_EVENT to read them.
Requesting AUTHORIZATION_EVENT in the same call gives you the full picture: every credential that declined, plus the final outcome.
FlexibleCredentialAttempts
Query
query FlexibleCredentialAttempts($id: ID!) {
node(id: $id) {
... on DebitTransaction {
id
transactionEvents(
eventTypes: [FLEXIBLE_CREDENTIAL_ATTEMPT_EVENT, AUTHORIZATION_EVENT]
) {
__typename
... on FlexibleCredentialAttemptEvent {
id
createdAt
responseCode
requestedAmount {
value
currencyCode
}
paymentCard {
id
last4
}
spendRuleResults(first: 20) {
edges {
node {
recommendation
message
attachedLevel
}
}
}
}
... on AuthorizationEvent {
id
createdAt
responseCode
approvedAmount {
value
currencyCode
}
paymentCard {
id
last4
}
}
}
}
}
}
Variables
{ "id": "DEBIT_TRANSACTION_ID" }
Result
{
"data": {
"node": {
"id": "DEBIT_TRANSACTION_ID",
"transactionEvents": [
{
"__typename": "FlexibleCredentialAttemptEvent",
"id": "FLEXIBLE_CREDENTIAL_ATTEMPT_EVENT_ID",
"createdAt": "2026-04-01T15:30:00.000Z",
"responseCode": "DO_NOT_HONOR",
"requestedAmount": {
"value": 3500,
"currencyCode": "USD"
},
"paymentCard": {
"id": "PAYMENT_CARD_ID_SECONDARY",
"last4": "1881"
},
"spendRuleResults": {
"edges": [
{
"node": {
"recommendation": "BLOCK",
"message": "Merchant category not permitted on this account",
"attachedLevel": "ACCOUNT"
}
}
]
}
},
{
"__typename": "AuthorizationEvent",
"id": "AUTHORIZATION_EVENT_ID",
"createdAt": "2026-04-01T15:30:00.100Z",
"responseCode": "APPROVED",
"approvedAmount": {
"value": 3500,
"currencyCode": "USD"
},
"paymentCard": {
"id": "PAYMENT_CARD_ID_PRIMARY",
"last4": "4242"
}
}
]
}
}
}
The paymentCard object identifies which credential the attempt ran against, and responseCode gives the decline reason.
When a spend rule caused the fallback, spendRuleResults names the rule that produced it.
It is the observable counterpart to credential selection, where a decline for any reason moves to the next credential.
Attempt events do not settle. They share the approved event's transaction, so they show up as part of that transaction's history rather than as transactions of their own.
Pagination
The two connections on this page paginate on different objects.
| Connection | Paginates on | A cursor is valid for |
|---|---|---|
flexibleCredentials | the account holder | that account holder's list |
flexibleCredentialActivities | the flexible credential | the whole combined feed |
The mechanics are the same in every case.
Request up to 20 items with first, pass the previous page's endCursor as after, and check pageInfo.hasNextPage to know when to stop.
What differs is the scope of the cursor, which is why a cursor from one account holder's list cannot be used against the combined activity feed.