Highnote Query Language
Overview
The Highnote Query Language (HQL) provides programmatic search capabilities across Highnote entities using SQL-like syntax through the GraphQL API.
Specifically, HQL enables complex searches that would normally require multiple standard GraphQL requests. Money values are in the currency's minor units — USD 500 means $5.00, not $500. See HQL Conventions.
| Search Type | Example |
|---|---|
| Cross-field conditions | transactionAmount > USD 500 AND createdAt >= '2024-01-01' |
| List operations | accountId IN ('ac_ba22446a041287274d81af25bb83eeba46b1', 'ac_c022126bc2bb629545a988564e267d8982a5') |
| Custom metadata | customField='riskLevel:high' |
| Logical grouping | (accountStatus = 'ACTIVE' OR accountStatus = 'PENDING') AND productId = 'pd_4644868cd7b749fa82a4149d46d9827d' |
The API processes HQL queries through several search contexts: Financial Accounts, Payment Transactions (acquiring), Universal Transactions (issuing), and Transaction Batches and their entries (acquiring), each exposing context-specific searchable fields alongside common attributes. The field tables below cover the first three; for transaction batches and batch entries, see Transaction Batching.
HQL operates through the searchQueryLanguage field within GraphQL filterBy inputs:
{
filterBy: {
searchQueryLanguage: {
query: "transactionAmount > USD 100 AND accountStatus = 'ACTIVE'",
version: "VERSION_1"
}
}
}
Limitations
HQL is a standalone filter system that uses the searchQueryLanguage field within filterBy inputs.
If provided with other standard filters, HQL takes precedence and the other filters are ignored.
Minimum API version: The minimum supported Search API version is VERSION_1.
Rate limits: See API Rate Limiting for details.
Search latency: Highnote’s Search API has a latency period of up to two minutes.
HQL Conventions
The Highnote Query Language uses the following conventions:
- A term is a keyword used to search on attributes related to an entity.
- An expression is
<term><operator><value>. For example,transactionAmount > USD 100. - A value is unquoted when it is a bare number, a money value, or an identifier-shaped token of letters, digits, and underscores. A Highnote identifier is identifier-shaped, so
productId = pd_4644868cd7b749fa82a4149d46d9827dneeds no quotes. - A value must be wrapped in single quotes when it contains anything else, such as a space, a comma, a parenthesis, or a literal apostrophe. For example,
cardholderName = 'Jane Smith'is quoted because of the space. Quoting a value that does not require it is always safe. - A literal apostrophe inside a quoted value must be escaped, either with a backslash or by doubling it.
cardholderName = 'Tae\'Shayla Williams'andcardholderName = 'Tae''Shayla Williams'are both valid and equivalent. An unescaped apostrophe makes the query invalid. - A money value is a currency code followed by an amount, as in
transactionAmount > USD 500. Quoting it ('USD 500') is also accepted. ANDandORcarry no precedence. Conditions combine strictly left to right, soa OR b AND cevaluates as(a OR b) AND c. Group explicitly with parentheses when you mean anything else.
The amount in a money value is in the currency's minor units, never its major ones. transactionAmount > USD 500 matches transactions above $5.00, not $500.
- The scale follows the currency, so it is not always cents:
JPY 300is ¥300, andBHD 300is 0.300 BHD. - Write the amount as a whole number. A decimal such as
USD 500.00is rejected as an invalid money filter. - A comparison with
=,!=,>,>=,<, or<=also scopes results to the currency you name, sotransactionAmount > USD 500returns no EUR transactions.
Build query strings safely
An HQL query is parsed server-side against a defined grammar, and the string you send is
normalized before it is parsed: non-ASCII quote characters — U+2018, U+2019, U+201C and
U+201D — are converted to ASCII '. That conversion happens after any check your own code
ran, so a value carrying no ASCII quote at the moment you inspect it can still become a live
string delimiter by the time the query is parsed.
Because of that, validate every interpolated value against a positive allowlist — the exact keyspace you expect, such as a known identifier prefix followed by hexadecimal, or one member of a fixed set of status names — and reject everything else. Do not rely on escaping quote characters, and do not rely on stripping them: the character that ends up breaking out of a quoted string need not be present when you look for it.
This matters most where a filter is the only thing separating one of your customers from another. A query is always confined to your own organization — every request is scoped from the authenticated principal, and a query string cannot widen that — but within your organization the filter you build is the boundary. Treat any identifier that reaches you from a customer, a URL, or a form field as untrusted, and prefer an identifier your own backend resolved over one the caller supplied.
HQL Grammar
The following ANTLR 4 grammar is the authoritative definition of the query string you pass to searchQueryLanguage. It is published so that client libraries, query builders, and code generation tools can produce valid HQL directly rather than inferring the syntax from examples.
The grammar defines syntax only. It does not describe which fields a search context accepts, nor which operators a given field allows — a query can parse cleanly and still be rejected. See Operators for the operators that are restricted to specific fields, HQL Conventions for when a value needs quoting, and the field table in each search section for the fields that section supports.
grammar SearchQuery;
// Parser Rules
searchQuery : query? EOF ;
query : expression ( (AND | OR) expression )* ;
expression : term | '(' query ')' ;
term : NOT? comparison ;
comparison : IDENTIFIER (EQ | NEQ | GT | GTE | LT | LTE | PREFIX_EQ | FUZZY_EQ | LIKE) value
| IDENTIFIER IN '(' valueList ')'
| IDENTIFIER NOT IN '(' valueList ')';
valueList : value (',' value)* ;
value : STRING | NUMBER | money | IDENTIFIER | ALPHANUMERIC ;
money : IDENTIFIER NUMBER ;
// Lexer Rules
AND : 'AND' ;
OR : 'OR' ;
NOT : 'NOT' ;
IN : 'IN' ;
PREFIX_EQ : '^=' ;
FUZZY_EQ : '~=' ;
LIKE : 'LIKE' ;
EQ : '=' ;
NEQ : '!=' ;
GT : '>' ;
GTE : '>=' ;
LT : '<' ;
LTE : '<=' ;
IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* ;
NUMBER : [0-9]+ ('.' [0-9]+)? ;
ALPHANUMERIC : [a-zA-Z0-9_]+;
STRING : '\'' ( '\\\'' | '\'\'' | ~'\'' )* '\'' ;
WS : [ \t\r\n]+ -> skip ;
An empty query is legal and matches everything. The EOF anchor on searchQuery means the parser must consume the whole input: a malformed query is rejected outright rather than silently evaluated as the longest valid prefix.
Operators
| Operator | Description | Example |
|---|---|---|
= | Equal to | accountId = 'ac_ba22446a041287274d81af25bb83eeba46b1' |
!= | Not Equal to | accountId != 'ac_ba22446a041287274d81af25bb83eeba46b1' |
> | Greater Than | transactionAmount > USD 100 |
>= | Greater Than or Equal To | transactionAmount >= USD 100 |
< | Less Than | transactionAmount < USD 100 |
<= | Less Than or Equal To | transactionAmount <= USD 100 |
^= | Prefix match. Restricted to fields with fuzzy search enabled — see below | merchantName ^= 'Ac' |
~= | Fuzzy match, tolerant of edit distance. Restricted to fields with fuzzy search enabled — see below | merchantName ~= 'Acme' |
LIKE | Wildcard match against the full field value, using * and ?. Restricted to fields with fuzzy search enabled — see below | merchantName LIKE '*Acme*' |
IN | Returns entities that match any value in list | accountId IN ('ac_ba22446a041287274d81af25bb83eeba46b1', 'ac_c022126bc2bb629545a988564e267d8982a5') |
NOT | Negates a single comparison. Distinct from NOT IN | NOT accountStatus = 'ACTIVE' |
NOT IN | Returns entities that do not match any value in list | accountId NOT IN ('ac_og2234f70e5e4e384f1ca1f52b26aab8e7b5', 'ac_og22d44c3eb6e6f14929954ba278ffcd84ec') |
AND | Both sides of expression are valid | transactionAmount > USD 100 AND accountId = 'ac_ba22446a041287274d81af25bb83eeba46b1' |
OR | Either side of expression is valid | accountId = 'ac_ba22446a041287274d81af25bb83eeba46b1' OR accountId = 'ac_c022126bc2bb629545a988564e267d8982a5' |
( ) | Groups search terms together | last4 = 1234 AND (accountId = 'ac_ba22446a041287274d81af25bb83eeba46b1' OR accountId = 'ac_c022126bc2bb629545a988564e267d8982a5') |
Fuzzy, prefix, and wildcard search
^=, ~=, and LIKE are enabled on specific text fields only. Every other field rejects all three with an OPERATOR_NOT_SUPPORTED validation error, even though the query parses.
Today they are enabled on three Universal Transaction Search fields: merchantName, institutionName, and cardAcceptorLocation. No field in Account Search, Acquiring Search, or the transaction-batch contexts described in Transaction Batching accepts them.
All three operators are case-insensitive: merchantName ^= 'ac' and merchantName ^= 'AC' match the same values.
LIKE is a wildcard match against the full field value, not a substring search. merchantName LIKE 'Acme' matches only the exact value Acme. Add the wildcards yourself to match a substring:
merchantName LIKE '*Acme*'— the value containsAcmemerchantName LIKE 'Acme*'— the value starts withAcme
Use ? to match exactly one character.
Account Search
An account search applies to FinancialAccount attributes.
Returns FinancialAccount.
| Field | Description | Example |
|---|---|---|
| id (or accountId) | FinancialAccount | |
| productId | The Card Product associated to the Financial Account | |
| activeFeatures | The list of active features. Valid enum values: FinancialAccountFeatureType | activeFeatures='DIRECT_DEPOSIT' |
| primaryAccountHolder | Primary account holder of the Financial Account | ps_ah01 |
| primaryAccountAccountHolder | Account Id of the primary user | ac_c022126bc2bb629545a988564e267d8982a5 |
| primaryAccountHolderAccount | If authorized user, Account Id of the primary user account; else Account Id of the primary user | ps_ap0135e1c47a453c4656bb8da5fb2a60f976, |
| customField | Custom fields linked to Financial Account. Must be an exact match. | customField='myCustomField:myCustomValue' |
| accountStatus | Status of a Financial Account. Valid enum values: FinancialAccountStatus | |
| application | Application used to issue the Financial Account | |
| program | The Program associated with the Financial Account | |
| createdAt | Datetime (ISO 8601) the Financial Account was created |
Acquiring Search
An acquiring search applies to attributes of the following: PaymentTransaction, PaymentTransactionLifecycleStep, and PaymentTransactionEvent.
Returns PaymentTransaction.
| Field | Description | Example |
|---|---|---|
| id | Identifier of the PaymentTransaction | |
| transactionId | The parent transaction id, if applicable | |
| transactionAmount | Amount of the transaction | |
| accountId | Financial Accounts linked to the Payment Transaction and corresponding Steps and Events | |
| transactionSearchType | Narrows down the search to either a TRANSACTION or EVENT. | |
| type | Narrows down the search to a specific transaction type, using the same values as the Universal Transaction Search type field. Only acquiring transaction types match in this context. | type = 'acquiringPayment' |
| transactionSearchStatus | A normalized value that represents whether a transaction is COMPLETED, PENDING, or FAILED. Dependent on the lifecycle of the transaction. | |
| cardholderEmail | Email associated with the cardholder of a Payment Transaction. Must be an exact match. | |
| cardholderName | Name associated with the cardholder of a Payment Transaction. Must be an exact match. | |
| authorizationIdentifier | ID of the authorization response code provided by the processor for this PaymentTransaction on a successful authorization | |
| addressResponseCode | Address Verification Service result for the street address. Valid values are UNKNOWN, NOT_PERFORMED, NOT_PROVIDED, NO_MATCH, and MATCH. | addressResponseCode = 'MATCH' |
| postalResponseCode | Address Verification Service result for the postal code. Valid values are UNKNOWN, NOT_PERFORMED, NOT_PROVIDED, NO_MATCH, ZIP5_MATCH, ZIP9_MATCH, and POSTAL_CODE_MATCH. | postalResponseCode = 'ZIP5_MATCH' |
| last4 | Last 4 digits of the card used for the Payment Transaction | |
| orderId | Originating Payment Order that this transaction was initiated from | |
| productId | The Card Product that is associated with a transaction. Multiple Card Products may be associated with a transaction. | |
| contractId | The contractId used to make the payment | |
| transferAccountId | Financial Accounts on the transfers created when the transaction's Payment Instructions resolve | |
| transferId | Unique ID of the transfer | |
| disburseToId | The PaymentInstruction.disburseTo ID for the transaction | |
| actionDate | A normalized timestamp field that searches on the last updated date for a transaction. For example, within the lifecycle of a Transaction, searches on the latest createdAt of a TransactionEvent. | |
| completedAt | A normalized timestamp field that searches on when a transaction was completed, if applicable. For example, if a Transaction has gone through the entire payment lifecycle, completedAt can be used to search when the final TransactionEvent occurred. | |
| createdAt | The date and time, in ISO 8601 format, this object was created. | |
| updatedAt | The date and time, in ISO 8601 format, this object was updated. |
Universal Transaction Search
A universal transaction search applies to normalized values related to the transaction types listed under type below, as well as attributes specific to individual transaction types.
UTS searches span both issuing and acquiring transactions.
Universal Transaction Search does not cover every Issuing Transaction Type. Fee transfers, credit card transfers, reward point transfers, and crypto funding transfers are not searchable from this context.
Returns TransactionSearchResult.
| Field | Description | Applies To |
|---|---|---|
| id | Identifier of the transaction | All |
| transactionSearchStatus | A normalized value across all transaction types that represents whether a transaction is COMPLETED, PENDING, or FAILED. Dependent on the lifecycle of each transaction type. | All |
| transactionAmount | A normalized value that searches across all transaction types' amount values.. Ex: WireTransfer.amount, TransactionEvent.approvedAmount | All |
| transactionAmountCurrencyCode | Currency that the transaction was initiated with | All |
| transactionId | The parent transaction id, if applicable | All |
| accountHolderId | The AccountHolder that may be associated with a transaction. Multiple AccountHolders may be associated with a transaction. | All |
| transactionSearchType | Narrows down the search to either a TRANSACTION or EVENT. | All |
| type | Narrows down the search to a specific transaction type. Allowed values are interFinancialAccountTransfer, wireTransfer, manualAdjustment, achTransfer, cardTransaction, checkPayment, disbursement, acquiringPayment, instantNetworkTransaction, instantSettlementTransaction, realtimePaymentTransaction, accountReceivable | All |
| accountId | A FinancialAccount that may be associated with a transaction. Multiple FinancialAccounts can be associated with 1 transaction. | All |
| createdAt | When the transaction was created | All |
| actionDate | A normalized timestamp field that searches on the last updated date for a transaction. For example, within the lifecycle of a Transaction, searches on the latest createdAt of a TransactionEvent | All |
| completedAt | A normalized timestamp field that searches on when a transaction was completed, if applicable. For example, if a Transaction has gone through the entire payment lifecycle, completedAt can be used to search when the final TransactionEvent occurred. | All |
| productId | The CardProduct that is associated with a transaction. Multiple CardProducts may be associated with a transaction | All |
| last4 | The last 4 digits of the card used in the transaction | TransactionEvent types |
| cardProcessingType | The method or channel used to process the transaction. Valid enum values: cardProcessingType | TransactionEvent types |
| cardAcceptorIdentificationCode | Highnote assigned Merchant ID | TransactionEvent types |
| merchantName | Name of the merchant | TransactionEvent types |
| institutionName | Name of the institution | TransactionEvent types |
| merchantCategoryCode or mcc | Human friendly enums representing 4-digit ISO-18245 merchant category codes. Valid enum values: MerchantCategory | TransactionEvent types |
| cardAcceptorLocation | Geographic/address info about where the transaction took place | TransactionEvent types |
| transactionCountryCode | Country code where the transaction took place | TransactionEvent types |
| postedAmount | Final settled transaction amount charged to the cardholder's account | TransactionEvent types |
HQL Examples
Use the following mutation to list card product accounts, and then search by createdAt date.
query ListCardProductAccounts(
$id: ID!
$first: Int
$after: String
$filterBy: AccountHolderFinancialAccountsFilterInput
) {
node(id: $id) {
id
__typename
... on Organization {
accounts(first: $first, after: $after, filterBy: $filterBy) {
__typename
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
createdAt
updatedAt
accountStatus
accountAttributes
cardProduct {
id
}
cardProductApplication {
... on AccountHolderCardProductApplication {
accountHolderSnapshot {
... on USPersonAccountHolderSnapshot {
accountHolderCurrent {
id
name {
familyName
givenName
middleName
suffix
title
}
}
}
... on USBusinessAccountHolderSnapshot {
accountHolderCurrent {
id
businessProfile {
name {
legalBusinessName
doingBusinessAsName
}
}
}
}
}
}
}
owner {
... on USPersonAccountHolder {
id
name {
familyName
givenName
middleName
suffix
title
}
}
... on USBusinessAccountHolder {
id
businessProfile {
name {
legalBusinessName
doingBusinessAsName
}
}
}
... on Organization {
id
profile {
displayName
}
}
}
ledgers(ledgerNames: AVAILABLE_CASH) {
id
name
normalBalance
creditBalance {
value
currencyCode
decimalPlaces
}
debitBalance {
value
currencyCode
decimalPlaces
}
}
features {
enabled
... on SecuredCreditPaymentCardFinancialAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on FleetCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on CommercialCreditPayInFullCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on CreditCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on OnDemandFundingFinancialAccountFeature {
sourceFinancialAccount {
id
}
}
}
}
}
}
}
... on CardProduct {
accounts(first: $first, after: $after, filterBy: $filterBy) {
__typename
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
createdAt
updatedAt
accountStatus
accountAttributes
cardProductApplication {
... on AccountHolderCardProductApplication {
accountHolderSnapshot {
... on USPersonAccountHolderSnapshot {
accountHolderCurrent {
id
name {
familyName
givenName
middleName
suffix
title
}
}
}
... on USBusinessAccountHolderSnapshot {
accountHolderCurrent {
id
businessProfile {
name {
legalBusinessName
doingBusinessAsName
}
}
}
}
}
}
}
owner {
... on USPersonAccountHolder {
id
name {
familyName
givenName
middleName
suffix
title
}
}
... on USBusinessAccountHolder {
id
businessProfile {
name {
legalBusinessName
doingBusinessAsName
}
}
}
... on Organization {
id
profile {
displayName
}
}
}
ledgers(ledgerNames: AVAILABLE_CASH) {
id
name
normalBalance
creditBalance {
value
currencyCode
decimalPlaces
}
debitBalance {
value
currencyCode
decimalPlaces
}
}
features {
enabled
... on SecuredCreditPaymentCardFinancialAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on FleetCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on CommercialCreditPayInFullCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on CreditCardAccountFeature {
creditLimit {
currencyCode
decimalPlaces
value
}
}
... on OnDemandFundingFinancialAccountFeature {
sourceFinancialAccount {
id
}
}
}
}
}
}
}
}
}