query vaultTokensAuthenticated
Lists vault tokens newest first as a Relay-style cursor-paginated connection: those of the organization named in 'input.orgId', or of the caller's own account when none is named. Deleted tokens are omitted unless 'includeDeleted' is set.
Returns VaultTokenConnection!
Arguments
| Argument | Type | Description |
|---|---|---|
input | VaultTokensInput! |
Example request
curl -X POST 'https://graph.clientloop.com/' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <api-key>' \
-d '{
"query": "query VaultTokens($input: VaultTokensInput!) { vaultTokens(input: $input) { edges { node { id orgId contactId type status fingerprint externalRef expiresAt deletedAt createdAt updatedAt } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } }",
"variables": {
"input": {
"orgId": "abc123",
"first": 42,
"after": "example",
"last": 42,
"before": "example",
"type": "Card",
"status": "Active",
"contactId": "abc123",
"externalRef": "example",
"includeDeleted": true
}
}
}'const response = await fetch('https://graph.clientloop.com/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer <api-key>',
},
body: JSON.stringify({
query: `
query VaultTokens($input: VaultTokensInput!) {
vaultTokens(input: $input) {
edges {
node {
id
orgId
contactId
type
status
fingerprint
externalRef
expiresAt
deletedAt
createdAt
updatedAt
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
`,
variables: {
"input": {
"orgId": "abc123",
"first": 42,
"after": "example",
"last": 42,
"before": "example",
"type": "Card",
"status": "Active",
"contactId": "abc123",
"externalRef": "example",
"includeDeleted": true
}
},
}),
});
const { data, errors } = await response.json();<?php
$body = <<<'JSON'
{
"query": "query VaultTokens($input: VaultTokensInput!) { vaultTokens(input: $input) { edges { node { id orgId contactId type status fingerprint externalRef expiresAt deletedAt createdAt updatedAt } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } }",
"variables": {
"input": {
"orgId": "abc123",
"first": 42,
"after": "example",
"last": 42,
"before": "example",
"type": "Card",
"status": "Active",
"contactId": "abc123",
"externalRef": "example",
"includeDeleted": true
}
}
}
JSON;
$ch = curl_init('https://graph.clientloop.com/');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer <api-key>',
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
var body = """
{
"query": "query VaultTokens($input: VaultTokensInput!) { vaultTokens(input: $input) { edges { node { id orgId contactId type status fingerprint externalRef expiresAt deletedAt createdAt updatedAt } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } }",
"variables": {
"input": {
"orgId": "abc123",
"first": 42,
"after": "example",
"last": 42,
"before": "example",
"type": "Card",
"status": "Active",
"contactId": "abc123",
"externalRef": "example",
"includeDeleted": true
}
}
}
""";
var request = HttpRequest.newBuilder(URI.create("https://graph.clientloop.com/"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer <api-key>")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
var response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());using System.Net.Http;
using System.Text;
var body = """
{
"query": "query VaultTokens($input: VaultTokensInput!) { vaultTokens(input: $input) { edges { node { id orgId contactId type status fingerprint externalRef expiresAt deletedAt createdAt updatedAt } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } }",
"variables": {
"input": {
"orgId": "abc123",
"first": 42,
"after": "example",
"last": 42,
"before": "example",
"type": "Card",
"status": "Active",
"contactId": "abc123",
"externalRef": "example",
"includeDeleted": true
}
}
}
""";
using var client = new HttpClient();
using var content = new StringContent(body, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer <api-key>");
var response = await client.PostAsync("https://graph.clientloop.com/", content);
var result = await response.Content.ReadAsStringAsync();Types
input VaultTokensInput
Filter and pagination for the 'vaultTokens' query.
Pagination is Relay cursor-based, not page/offset based — see the Relay Connections spec. Page forward with first + after, or backward with last + before. Either of last or before selects the backward direction, and a request that combines the two directions is rejected as a validation error rather than paged in one of them. Cursors are opaque strings taken from a previous response's pageInfo (or edges[].cursor) — treat them as black boxes, don't build or parse them yourself.
| Field | Type | Description |
|---|---|---|
orgId | ID | Return only tokens belonging to this organization. Omit to list the tokens owned by the caller's own account instead: a platform or agency API key's tokens, or an org key's organization. A session must name one. |
first | Int | Forward pagination: return at most the first N tokens from the start of the result window. Pair with after to walk forward. Do not combine with last/before. When omitted (and no last), a default page size is used. |
after | String | Forward pagination cursor: return the tokens that come after this cursor. Use the pageInfo.endCursor from the previous page. Pair with first. |
last | Int | Backward pagination: return at most the last N tokens nearest the end of the result window. Pair with before to walk backward. Do not combine with first/after. |
before | String | Backward pagination cursor: return the tokens that come before this cursor. Use the pageInfo.startCursor from the previous page. Pair with last. |
type | VaultTokenType | Return only tokens of this kind. Omit to return both cards and bank accounts. |
status | VaultTokenStatus | Return only tokens in this state. Omit to return every state. |
contactId | ID | Return only tokens linked to this contact. |
externalRef | String | Return only tokens carrying this externalRef. |
includeDeleted | Boolean | Include tokens that have been deleted. Defaults to false, so deleted tokens are omitted. Deleted tokens can never be used; include them only to render history. Status is still judged for a deleted token, so the status filter applies to it too. |
type VaultTokenConnection
A page of vault tokens following the Relay Connection spec. edges holds the tokens in this page (each with its cursor) and pageInfo describes whether more pages exist in each direction plus the cursors that bound this page.
| Field | Type | Description |
|---|---|---|
edges | [VaultTokenEdge!]! | The vault tokens in this page, ordered newest first. |
pageInfo | PageInfo! | Pagination metadata for this page: hasNextPage/hasPreviousPage and the startCursor/endCursor bounds. |
enum VaultTokenType
The kind of instrument a vault token stands for.
Card— A credit or debit card.BankAccount— A bank account debited over ACH.
enum VaultTokenStatus
Whether the instrument behind a vault token is still current, judged each time it is read. Deletion is separate and told apart by 'deletedAt': a deleted token still reports a status, and can still move to Expired, so a token is usable only while its status is Active and it has not been deleted.
Active— The instrument is current.Expired— Past expiresAt without having been used, or past the card's expiration date. The token can no longer be used; tokenize the instrument again to get a new one.
type VaultTokenEdge
A single element of a VaultTokenConnection page: the token itself (node) plus the opaque cursor that points at it. Pass a cursor back as 'after' (forward) or 'before' (backward) to page relative to this row.
| Field | Type | Description |
|---|---|---|
node | VaultToken! | The vault token at this position in the page. |
cursor | String! | Opaque cursor identifying this token's position, for use as after or before. |
type PageInfo
PageInfo type for cursor-based pagination following the Relay specification for cursor based pagination.
| Field | Type | Description |
|---|---|---|
hasNextPage | Boolean! | |
hasPreviousPage | Boolean! | |
startCursor | String | |
endCursor | String |
type VaultToken
A card or bank account held in the ClientLoop vault, referenced by an opaque token. A third party tokenizes an instrument once with 'vaultTokenizeCard' or 'vaultTokenizeBankAccount' and presents the token later in place of the account data.
A token is short-lived until it is used: one not used to create a payment or payment plan within an hour of being created is deleted, together with the instrument it holds. A token that has been used lives as long as the payment or plan needs it.
A vault token is not a stored payment method. It is optionally attached to an organization and to a contact, and carries no consent or recurring arrangement of its own; keeping an instrument on file for a contact is what 'PaymentMethod' is for. The account number itself is never returned: a token exposes the BIN, last four digits, expiration and brand of a card, or the routing number and last four digits of a bank account.
| Field | Type | Description |
|---|---|---|
id | ID! | Opaque id of the vault token. Refetch it with 'vaultToken(id:)'. |
orgId | ID | Organization the token belongs to: the one named when it was created, or an org API key's own organization. Null for a token a platform or agency key created without naming one, which belongs to that account instead. |
contactId | ID | ID of the contact the token is linked to, when one was given. |
contact | Contact | Contact the token is linked to, when one was given. |
type | VaultTokenType! | Whether the token stands for a card or a bank account. Determines which of 'card' and 'bankAccount' is populated. |
status | VaultTokenStatus! | Whether the instrument is still current: Active, or Expired once the token's expiresAt or the card's expiration date has passed. Deletion is reported by 'deletedAt', not here. |
card | VaultTokenCard | Card detail. Populated when type is Card, otherwise null. |
bankAccount | VaultTokenBankAccount | Bank account detail. Populated when type is BankAccount, otherwise null. |
billingAddress | GlobalAddress | Billing address captured alongside the instrument, when one was given. |
fingerprint | String | Stable, non-reversible identifier of the underlying card or account within the token's owner — the organization, or the platform or agency account an org-less token belongs to: the same instrument tokenized twice for that owner yields the same fingerprint, so a caller can recognise a repeat without seeing the number. |
externalRef | String | Caller-supplied reference for the token, external to the platform. Ex. an order or account id. |
expiresAt | DateTime | When the token is deleted unless it has been used to create a payment or payment plan by then: one hour after it was created. Null once the token has been used. A token past this time is removed outright rather than kept in a deleted state. |
deletedAt | DateTime | Set when the token has been deleted; null otherwise. A deleted token can no longer be used whatever its status says, and is excluded from listings unless 'includeDeleted' is set. |
createdAt | DateTime! | Date the token was created. |
updatedAt | DateTime! | Date the token was last updated. |
type Contact
| Field | Type | Description |
|---|---|---|
id | ID! | Unique ID of the contact. |
orgId | ID! | ID of the organization that owns the contact. |
ownerId | ID! | Global ID of the owning organization ( |
name | String! | Full name of the contact. |
givenName | String | Given name of the contact. |
familyName | String | Family name of the contact. |
email | Email | Email address of the contact. |
phone | Phone | Phone number of the contact. This will be validated and normalized to the E.164 format. |
deletedAt | String | Set when the contact has been soft-deleted; null for an active contact. |
createdAt | String! | Date and time when the contact was created. |
updatedAt | String! | Date and time when the contact was last updated. |
idv | ContactIdvSessionDetail | The latest Plaid identity-verification session for this contact. On the public graph this exposes the session timestamps, the captured selfie video, the captured identity documents, and the individual check outcomes; the remaining detail is private-graph only. Null when the contact has never started a session. Fetched on demand from Plaid — request it only when needed. |
paymentMethods(input) | PaymentMethodConnection! | Payment methods stored against this contact, as a Relay-style, cursor-paginated connection, newest first. Page forward by starting with first: N, then passing the previous response's pageInfo.endCursor back as after (repeat while pageInfo.hasNextPage); page backward with last + before. Deleted methods are omitted unless includeDeleted is set. |
vaultTokens(input) | VaultTokenConnection! | Vault tokens linked to this contact, as a Relay-style, cursor-paginated connection, newest first. Always scoped to this contact: a token is linked by giving 'contactId' when it is created, and tokens created without one do not appear here — list those with the 'vaultTokens' query instead. Page forward by starting with first: N, then passing the previous response's pageInfo.endCursor back as after (repeat while pageInfo.hasNextPage); page backward with last + before. Deleted tokens are omitted unless includeDeleted is set. |
type VaultTokenCard
Card detail for a vault token whose type is Card.
| Field | Type | Description |
|---|---|---|
bin | String | First six digits of the card number. |
last4 | String! | Last four digits of the card number. |
brand | String | Card brand identified from the number's issuer range. Ex. Visa. Null when the range is not one the platform recognises. |
expirationMonth | Int | Expiration month, 1-12. Null when it was not supplied. |
expirationYear | Int | Expiration year, four digits. Ex. 2029. Null when it was not supplied. |
cardholderName | String | Name on the card, when it was supplied. |
funding | PaymentMethodCardFunding! | Whether the card draws on a credit, debit or prepaid account. Reported only when the provider's BIN data carries it; otherwise Unknown. |
type VaultTokenBankAccount
Bank account detail for a vault token whose type is BankAccount.
| Field | Type | Description |
|---|---|---|
routingNumber | String! | Nine digit ABA routing number. Identifies the bank, not the account, so it is kept in the clear. |
last4 | String! | Last four digits of the account number. |
accountType | PaymentMethodBankAccountType! | Whether the account is a checking or savings account. Checking unless Savings was supplied. |
holderType | VaultBankAccountHolderType! | Whether the account belongs to a person or a business. Personal unless Business was supplied. |
accountHolderName | String | Name on the account, when it was supplied. |
type GlobalAddress
Address of a physical location
| Field | Type | Description |
|---|---|---|
lines | [String!]! | Street, unit, building number, etc. |
locality | String | City, town or municipality designation |
administrativeArea | String | State, province or area designation |
postalCode | String | Postal code or ZIP code |
countryCode | String! | 2-letter country code. Ex. USA |
scalar DateTime
ISO 8601 formatted date time. Ex. 2023-11-23T14:30:00Z
scalar Email
An email address
scalar Phone
E.164 formatted phone number. Ex. +14155554345
type ContactIdvSessionDetail
Details of a contact's Plaid identity-verification session, fetched on demand from Plaid. The public graph exposes the timestamps, the captured selfie video, the captured identity documents, and the individual check outcomes; the remaining fields — including the raw Plaid pass-throughs they are derived from — are private-graph only.
| Field | Type | Description |
|---|---|---|
status | ContactIdvSessionStatus! | Where the verification as a whole stands. Success, Failed and PendingReview are all terminal and all carry captured identity; Active is still in progress, and Expired or Canceled never produced one. |
createdAt | DateTime! | |
completedAt | DateTime | |
documents | [ContactIdvDocument!]! | Captured identity documents, pulled out of documentary_verification: each document's category and its captured images. |
selfieVideoUrl | String | URL of the captured selfie video, pulled out of selfieCheck for direct access. Plaid-hosted and expiring; null when the template did not capture a selfie video. |
nameMatch | IdvMatchSummary | How the name that the contact supplied compared against Plaid's data sources. Null when the KYC step has not run. |
dateOfBirthMatch | IdvMatchSummary | How the date of birth compared against Plaid's data sources. Null when the KYC step has not run. |
phoneNumberMatch | IdvMatchSummary | How the phone number compared against Plaid's data sources. Null when the KYC step has not run. |
addressMatch | IdvMatchSummary | How the address compared against Plaid's data sources. Null when the KYC step has not run. |
taxIdMatch | IdvMatchSummary | How the tax id (SSN) compared against Plaid's data sources, from Plaid's id_number check. Null when the KYC step has not run. |
livenessCheck | IdvLivenessStatus | Whether the captured selfie passed liveness detection. Null when the selfie step has not run or captured no analysis. |
facialComparisonCheck | IdvFacialComparisonStatus | Whether the captured selfie matched the face on the identity document. Null when the selfie step has not run or captured no analysis. |
type PaymentMethodConnection
A page of payment methods following the Relay Connection spec. edges holds the payment methods in this page (each with its cursor) and pageInfo describes whether more pages exist in each direction plus the cursors that bound this page.
| Field | Type | Description |
|---|---|---|
edges | [PaymentMethodEdge!]! | The payment methods in this page, ordered newest first. |
pageInfo | PageInfo! | Pagination metadata for this page: hasNextPage/hasPreviousPage and the startCursor/endCursor bounds. |
input PaymentMethodsInput
Filter and pagination for a payment methods connection.
Pagination is Relay cursor-based, not page/offset based — see the Relay Connections spec. Page forward with first + after, or backward with last + before; never mix the two directions in a single call. Cursors are opaque strings taken from a previous response's pageInfo (or edges[].cursor) — treat them as black boxes, don't build or parse them yourself.
| Field | Type | Description |
|---|---|---|
first | Int | Forward pagination: return at most the first N payment methods from the start of the result window. Pair with after to walk forward. Do not combine with last/before. When omitted (and no last), a default page size is used. |
after | String | Forward pagination cursor: return the payment methods that come after this cursor. Use the pageInfo.endCursor from the previous page. Pair with first. |
last | Int | Backward pagination: return at most the last N payment methods nearest the end of the result window. Pair with before to walk backward. Do not combine with first/after. |
before | String | Backward pagination cursor: return the payment methods that come before this cursor. Use the pageInfo.startCursor from the previous page. Pair with last. |
type | PaymentMethodType | Return only methods of this kind. Omit to return both cards and bank accounts. |
status | PaymentMethodStatus | Return only methods in this state. Omit to return every state. |
includeDeleted | Boolean | Include methods that have been deleted. Defaults to false, so deleted methods are omitted. Deleted methods can never be charged; include them only to render history. |
input ContactVaultTokensInput
Filter and pagination for 'Contact.vaultTokens'. The contact is the one the field hangs off, so there is no contact or organization filter to pass. Pagination works as on 'VaultTokensInput': forward with first + after, backward with last + before, and a request that combines the two directions is rejected as a validation error.
| Field | Type | Description |
|---|---|---|
first | Int | Forward pagination: return at most the first N tokens from the start of the result window. Pair with after to walk forward. Do not combine with last/before. When omitted (and no last or before), a default page size is used. |
after | String | Forward pagination cursor: return the tokens that come after this cursor. Use the pageInfo.endCursor from the previous page. Pair with first. |
last | Int | Backward pagination: return at most the last N tokens nearest the end of the result window. Pair with before to walk backward. Do not combine with first/after. |
before | String | Backward pagination cursor: return the tokens that come before this cursor. Use the pageInfo.startCursor from the previous page. Pair with last. |
type | VaultTokenType | Return only tokens of this kind. Omit to return both cards and bank accounts. |
status | VaultTokenStatus | Return only tokens in this state. Omit to return every state. |
externalRef | String | Return only tokens carrying this externalRef. |
includeDeleted | Boolean | Include tokens that have been deleted. Defaults to false, so deleted tokens are omitted. Status is still judged for a deleted token, so the status filter applies to it too. |
enum PaymentMethodCardFunding
How a stored card funds a payment, when the provider reports it.
CreditDebitPrepaidUnknown
enum PaymentMethodBankAccountType
The kind of bank account a stored payment method debits.
CheckingSavings
enum VaultBankAccountHolderType
Whether a bank account belongs to a person or a business.
PersonalBusiness
enum ContactIdvSessionStatus
Where a contact's verification stands. Mirrors the shared IdvSessionStatus but is declared separately so the contact graph's public surface does not depend on a type owned by the application module.
ActiveExpiredCanceledSuccessFailedPendingReview
type ContactIdvDocument
A captured identity document from a contact's Plaid documentary verification.
| Field | Type | Description |
|---|---|---|
category | String | Document category as classified by Plaid (e.g. drivers_license, id_card, passport). Null when Plaid could not classify the document. |
images | [ContactIdvDocumentImage!]! | Captured images for this document (e.g. originalFront, croppedBack, face). |
enum IdvMatchSummary
How one value that the contact supplied compared against the data sources that Plaid checked it against. NoData means Plaid held nothing to compare with; NoInput means the contact supplied nothing to compare.
MatchPartialMatchNoMatchNoDataNoInput
enum IdvLivenessStatus
Whether the captured selfie passed liveness detection — that a live person was present rather than a photograph or a screen.
SuccessFailed
enum IdvFacialComparisonStatus
How the captured selfie compared against the face on the captured identity document. NoInput means one of the two was never captured.
MatchNoMatchNoInput
type PaymentMethodEdge
A single element of a PaymentMethodConnection page: the payment method itself
(node) plus the opaque cursor that points at it. Pass a cursor back as
after (forward) or before (backward) to page relative to this row.
| Field | Type | Description |
|---|---|---|
node | PaymentMethod! | The payment method at this position in the page. |
cursor | String! | Opaque cursor identifying this payment method's position, for use as after or before. |
enum PaymentMethodType
The kind of instrument a stored payment method holds.
Card— A credit or debit card.BankAccount— A bank account debited over ACH.
enum PaymentMethodStatus
Whether a stored payment method can still be charged.
Active— Usable for new payments.Expired— Past its expiration date. Charges will be declined until the customer stores a new method.Invalid— The provider no longer accepts the stored credential, for example because the card was reported lost or the bank account was closed.
type ContactIdvDocumentImage
A single captured image belonging to a ContactIdvDocument. Plaid-hosted and expiring.
| Field | Type | Description |
|---|---|---|
name | String | Image identifier (e.g. originalFront, croppedBack, face). |
url | String | Plaid-hosted URL of the image. Expires. |
type PaymentMethod
A payment instrument stored against a contact so it can be charged again without the customer re-entering it.
A payment method is created by completing a payment session whose
storePaymentMethod was not Disabled, and charged afterwards with
chargePaymentMethod.
Payment options that are unsuitable for storing are not offered during such a
session — consumer financing such as FlexPay, for example, applies to a
single purchase and cannot act as a method on file.
| Field | Type | Description |
|---|---|---|
id | ID! | Globally unique id of the payment method, a KSUID behind the |
orgId | ID! | Organization that owns this payment method. |
contactId | ID! | ID of the contact this payment method is stored against. |
contact | Contact | Contact this payment method is stored against. |
type | PaymentMethodType! | Whether this method is a card or a bank account. Determines which of
|
status | PaymentMethodStatus! | Whether the method can still be charged. |
provider | PaymentProvider! | Payment provider holding the underlying credential. |
configurationId | ID | ID of the provider configuration the credential is stored under. Charges against this method run through the same configuration. |
card | PaymentMethodCard | Card detail. Populated when type is Card, otherwise null. |
bankAccount | PaymentMethodBankAccount | Bank account detail. Populated when type is BankAccount, otherwise null. |
supportedRecurringProcessingModels | [RecurringProcessingModel!]! | The ways this method may be used for a later payment. A method is stored under the model declared on its payment session, and the provider reports which models the resulting credential supports. Read it before offering a customer a subscription against a method that may not carry one. |
billingAddress | GlobalAddress | Billing address captured alongside the instrument. |
paymentSessionId | ID | ID of the payment session that stored this payment method. |
deletedAt | DateTime | Set when the payment method has been deleted; null while it is usable. A
deleted method is excluded from |
createdAt | DateTime! | Date the payment method was stored. |
updatedAt | DateTime! | Date the payment method was last updated. |
enum PaymentProvider
NMIPlaidCLP
type PaymentMethodCard
Card detail for a stored payment method whose type is Card.
| Field | Type | Description |
|---|---|---|
brand | String | Card brand as reported by the provider. Ex. Visa |
last4 | String! | Last four digits of the card number. |
expirationMonth | Int! | Expiration month, 1-12. |
expirationYear | Int! | Expiration year, four digits. Ex. 2029 |
cardholderName | String | Name on the card as captured when the method was stored. |
funding | PaymentMethodCardFunding! | Whether the card draws on a credit, debit or prepaid account. Surcharging rules turn on this distinction. It can only be captured from the provider's response at the moment the method is stored, never looked up afterwards, and some providers report it only when the merchant's account is configured to include it, so a method stored without it stays Unknown for life. |
type PaymentMethodBankAccount
Bank account detail for a stored payment method whose type is BankAccount.
| Field | Type | Description |
|---|---|---|
institutionName | String | Name of the institution holding the account. Ex. Chase. Depends on the provider: reported for accounts linked through Plaid, and generally absent for accounts stored directly with the card processor, which does not return it. |
accountHolderName | String | Name on the account as reported by the provider. |
last4 | String | Last four digits of the account number. |
accountType | PaymentMethodBankAccountType | Whether the account is a checking or savings account. |
enum RecurringProcessingModel
How a stored payment method may be used for a later payment, following the card networks' stored-credential framework. The model is declared when a method is stored and again on every payment made from it, and it decides how that later payment is treated for Strong Customer Authentication: a Subscription or UnscheduledCardOnFile charge is one you initiate and falls outside SCA, while a CardOnFile charge does not.
What separates the two models you initiate is the schedule, not the amount. A run of payments on a fixed interval is a Subscription even when the amount differs every time.
CardOnFile— Details kept so a returning customer checks out faster, where the later payment is one the customer starts themselves — a "pay with my saved card" button in your own checkout, for example.Subscription— You initiate the payment, and the payments follow a fixed schedule the customer agreed to in advance. The amount may be fixed or may vary from one charge to the next: a monthly invoice whose total changes every month is still a subscription, because it is the interval that is fixed.UnscheduledCardOnFile— You initiate the payment, and the payments follow no fixed schedule. For example a top-up triggered by a balance falling below a threshold, or an invoice raised whenever a job is finished.