Filterable API Collections
Introduction
Numerous GraphQL queries that return collections of objects (such as self-hosted wallet proofs or VASP-to-VASP transactions) accept identically structured filtering parameters that can be used to select a sub-set of items. This section explains how those filter parameters are structured, and walks through some examples.
It may also be instructive to see how the 21 Compliance Dashboard leverages these filter APIs to present numerous filtering options to the user. This is explained in the corresponding GUI section.
Recursive filter
structure
In general, any query for filtering results can be expressed as a nested
combination of AND
, OR
and NOT
formulas. The root filter
parameter
accepted by filterable APIs allows you to specify a filter condition that can
represent such an arbitrarily nested formula. Taking the AopdProofFilterInput
input as an example, it is defined as:
input AopdProofFilterInput @oneOf {
and: [AopdProofFilterInput!]
or: [AopdProofFilterInput!]
not: AopdProofFilterInput
is: AopdProofConditionInput
}
The union type has three recursive and
, or
and not
variants that start a
new boolean formula, and one terminating variant is
, where an actual condition
can be specified. Abbreviating the terminating objects of the
AopdProofConditionInput
type as C1
, C2
, etc., a valid filter
object
could thus look like:
filter: {
and: [
{ is: C1 },
{ not: { is: C2 } },
{ or: [ { is: C3 }, { and: [ { is: C4 }, { not: { is: C5 } } ] } ] }
]
}
In the next section, we will look at how those Cx
filter conditions can be
constructed.
Filtering Heterogeneous vs. Homogeneous Collections
Again taking the filter for self-hosted wallet proofs as an example, let's look
at the AopdProofConditionInput
type, which terminates the recursive filter
formulas for AopdProofFilterInput
, as an example. Since self-hosted wallet
proofs can be of three different types, this is again a union with variants
corresponding to the different proof types:
input AopdProofConditionInput @oneOf {
signature: AopdSignatureProofConditionInput
media: AopdMediaProofConditionInput
satoshi: AopdSatoshiProofConditionInput
any: AopdAnyProofConditionInput
}
Choosing one of the first three variants will automatically restrict filter
results for this condition to the chosen proof type. In addition, there is an
any
variant which collects filtering criteria applicable to all proof types,
and which doesn't restrict the type of returned proofs.
Some filter APIs only return objects of one particular type, such as the filter for counterparty VASPs. In this case, this intermediate union is skipped, and the recursive filter is terminated by an object type where conditions can be specified.
Next, we will look at the actual filtering condition types, e.g. the kinds of
types pointed at by the AopdProofConditionInput
variants above.
Specifying Filter Conditions
Again taking the filter for self-hosted wallet proofs as an example, let's look
at the abbreviated definition of AopdSatoshiProofConditionInput
:
input AopdSatoshiProofConditionInput {
asset: TravelStringConditionInput
amount: TravelAmountConditionInput
status: [AopdProofStatus!]
createdAt: TravelTimestampConditionInput
...
}
The first thing to notice is that all fields are optional, representing optional restrictions on some attribute of a Satoshi proof. In fact, using the empty object as:
filter: { is: { satoshi: {} } }
has the meaningful effect of restricting the results to Satoshi proofs.
More likely though, you will want to use a subset of the fields to specify some conditions on a Satoshi proof. The types used for this fall into two categories, detailed next.
Condition Types
For numerous types, there exist union Condition
types which are shared across
the whole schema and whose variants express a constraint. For the above example:
-
For strings:
TravelStringConditionInput
, whoseIlike
variant allows you to filter for sub-strings (see the type's GraphQL documentation for more information). -
For amounts:
TravelAmountConditionInput
, which has variants to express 'less than X', 'equal to X' and 'larger than X'. -
For timestamps:
TravelTimestampConditionInput
, where a 'before X' or 'after X' constraint can be expressed.
Filter for Enumerable Fields
In the above example, every Satoshi proof has a status
of union type
AopdProofStatus
. The available filter for this property allows you to specify
a list of statuses, where the Satoshi proof status must match one of them.
A Complete Example
As a concluding example, the following query will return all proof IDs for proofs that are:
-
Of any type and verified, or
-
Signature proofs created after 2024 whose address start with "0x", or
-
Media proofs whose comment contains "OK" and whose customer ID does not contain "LEGACY".
query {
aopd {
proofs(
filter: {
or: [
{ is: { any: { status: VERIFIED } } }
{
is: {
signature: {
address: { ilike: "0x%" }
createdAt: { after: "2025-01-01T00:00:00" }
}
}
}
{
and: [
{ is: { media: { comment: { ilike: "%OK%" } } } }
{ not: { is: { media: { customerId: { ilike: "%LEGACY%" } } } } }
]
}
]
}
) {
__typename
... on AopdMediaProof {
id
}
... on AopdSatoshiProof {
id
}
... on AopdSignatureProof {
id
}
}
}
}