POST /search— returns a paginated list of matching contactsPOST /kpi— returns only the count of matching contacts, with no contact data
How queries are structured
Every search query is a two-level tree. The outer group sets the top-level logic; each inner group contains the individual conditions.$all— ALL conditions inside must match (AND logic)$at_least_one— AT LEAST ONE condition must match (OR logic)
Operators
| What you want to say | ope value |
|---|---|
| equals | eql |
| does not equal | not_eql |
| has a value / is filled in | ext |
| has no value / is empty | not_ext |
| is before / at most | lte |
| is after / at least | gte |
| is between two values | range — use with from and to |
| contains the text | contains |
| does not contain the text | not_contains |
| starts with | start_with |
| does not start with | not_start_with |
| equals (case-sensitive, exact match) | eql:strictdata |
| matches any of a list | any_of |
| matches none of a list | none_of |
| matches all of a list | all_of |
"now", "now-30d", "now-1y", "now+1y". All dates are interpreted as UTC.
Pagination and sorting
| Parameter | Description |
|---|---|
per_page | Contacts per page — required, 1–1000 |
page | Page number (default: 0) |
sort_attr | Sort by: surname, firstname, birthdate, gender, lastchange, mail, married_name, city |
sort_asc | true = ascending order (default: false) |
include_interactions | true = include each contact’s interaction history in the response |
Getting a count without results
UsePOST /kpi with the same query body to get the number of matching contacts instantly without fetching the full contact list.
{ "data": { "total": 1042 }, "status": "success" }
Examples
Active memberships today
Active memberships today
Contacts whose membership started on or before today and ends on or after today.
Donors with cumulative amount ≥ €100
Donors with cumulative amount ≥ €100
Amount values are always passed as strings in euros.
Email delivered but not opened (campaign 42)
Email delivered but not opened (campaign 42)
Two independent AND groups
Two independent AND groups
Members in 2024 who also made at least one donation.
OR on cities combined with an AND condition
OR on cities combined with an AND condition
In Paris or Lyon, and has a mobile number.
Filter by form answer (consent, custom field…)
Filter by form answer (consent, custom field…)
Use
GET /v1/forms/type/{type} to find your form_id, then GET /forms/{id} for form_ref_ids.Attribute reference
Contact fields
| Attribute | Meaning | Operators |
|---|---|---|
firstname | First name | eql, not_eql, ext, not_ext, start_with, not_start_with |
surname | Last name | eql, not_eql, ext, not_ext, start_with, not_start_with |
married_name | Married name | eql, not_eql, ext, not_ext, start_with, not_start_with |
gender | Gender (M, F, O) | eql, not_eql, ext, not_ext |
birthdate | Date of birth | eql, ext, not_ext, range, lte, gte |
age_cat | Age category (0–6) | eql, not_eql, ext, not_ext |
birthcity | City of birth | eql, not_eql, ext, not_ext |
birthcountry | Country of birth | eql, not_eql, ext, not_ext |
nationality | Nationality | eql, not_eql, ext, not_ext |
mail | Email address | eql:strictdata, not_eql:strictdata, ext, not_ext, start_with, not_start_with |
phone | Landline | eql, not_eql, ext, not_ext, start_with, not_start_with |
mobile | Mobile number | eql, not_eql, ext, not_ext, contains, start_with, not_start_with |
tags.name | Tag name | eql, not_eql, any_of, none_of, all_of, ext, not_ext |
black_list | Blocked from all communication | eql, not_eql |
lastchange | Last modification date | eql, ext, not_ext, range, lte, gte |
user_id | ID of last user who edited the contact | eql, not_eql, ext, not_ext |
user_contact_id | ID of the linked Qomon user | ext, not_ext |
action_ids | IDs of linked actions | eql, not_eql |
Address fields
| Attribute | Meaning | Operators |
|---|---|---|
address.city | City | eql, not_eql, ext, not_ext, start_with, not_start_with |
address.postalcode | Postal code | eql, not_eql, ext, not_ext, start_with, not_start_with |
address.country | Country | eql, not_eql, ext, not_ext |
address.street | Street name | eql, not_eql, ext, not_ext, start_with, not_start_with |
address.housenumber | House number | eql, not_eql, ext, not_ext |
address.building | Building | eql, not_eql, ext, not_ext |
address.floor | Floor | eql, not_eql, ext, not_ext |
address.door | Door | eql, not_eql, ext, not_ext |
address.addition | Address complement | eql, not_eql, ext, not_ext |
address.pollingstation | Polling station | eql, not_eql, any_of, none_of, ext, not_ext |
address.invalid | Address flagged as invalid | eql, not_eql, ext, not_ext |
Financial fields
These fields match contacts who have at least one matching record. For example,
transactions.amount gte "50" returns contacts with at least one transaction ≥ €50, not contacts whose total is ≥ €50 — use sum_amount for totals. Amount fields are in euros and must always be passed as strings: "value": "50" not "value": 50.transactions.*)
| Attribute | Meaning | Operators |
|---|---|---|
transactions.created_at | Record creation date | lte, gte, range |
transactions.date | Transaction execution date | eql, not_eql, lte, gte, range |
transactions.amount | Amount of one transaction (€) | eql, not_eql, lte, gte, range |
transactions.sum_amount | Total amount across all transactions (€) | eql, not_eql, lte, gte, range |
transactions.count_amount | Number of transactions | eql, not_eql, lte, gte, range |
transactions.code_campaign | Campaign code | ext, not_ext, eql, not_eql, contains, not_contains, start_with, not_start_with |
transactions.payment_method_kind | Payment method | eql, not_eql |
transactions.status_id | Transaction status ID | eql, not_eql |
donations.*)
| Attribute | Meaning | Operators |
|---|---|---|
donations.created_at | Record creation date | eql, not_eql, lte, gte, range |
donations.transaction_date | Donation operation date | eql, not_eql, lte, gte, range |
donations.amount | Amount of one donation (€) | eql, not_eql, lte, gte, range |
donations.sum_amount | Total donated (€) | eql, not_eql, lte, gte, range |
donations.count_amount | Number of donations | eql, not_eql, lte, gte, range |
donations.donation_price_id | Donation type/tier ID | eql, not_eql |
memberships.*)
| Attribute | Meaning | Operators |
|---|---|---|
memberships.created_at | Record creation date | eql, not_eql, lte, gte, range |
memberships.start_date | Membership start date | eql, not_eql, lte, gte, range |
memberships.end_date | Membership end date | eql, not_eql, lte, gte, range |
memberships.transaction_date | Membership operation date | eql, not_eql, lte, gte, range |
memberships.period | Active year — e.g. "2024" means active that calendar year | eql, not_eql, lte, gte, range |
memberships.multiple_in_period | Had more than one membership in a given year | eql (value = year string e.g. "2024") |
memberships.membership_price_id | Membership type/tier ID | eql, not_eql |
Email engagement fields
These match contacts who have at least one email record where that field is set. A contact with no email history will not match
ext or not_ext.| Attribute | Meaning | Operators |
|---|---|---|
emails.created_at | Email record date | ext, not_ext, lte, gte, range |
emails.type | Email type | ext, not_ext, eql, not_eql |
emails.sender | Sender address | ext, not_ext, eql, not_eql |
emails.subject | Email subject line | ext, not_ext, contains, not_contains, start_with, not_start_with |
emails.user_id | ID of Qomon user who sent the campaign | ext, not_ext, eql, not_eql |
emails.campaign_id | Campaign ID | ext, not_ext, eql, not_eql |
emails.campaign_name | Campaign name | ext, not_ext, contains, not_contains, start_with, not_start_with |
emails.campaign_goal | Campaign goal | ext, not_ext, eql, not_eql |
emails.campaign_is_successful | Campaign marked successful | ext, not_ext, eql, not_eql |
emails.campaign_performance_rating | Campaign performance score (0–100) | ext, not_ext, lte, gte, range |
emails.template_id | Template ID | ext, not_ext, eql, not_eql |
emails.template_name | Template name | ext, not_ext, contains, not_contains, start_with, not_start_with |
emails.delivered_at | Email delivered date | ext, not_ext, lte, gte, range |
emails.opened_at | Email opened date | ext, not_ext, lte, gte, range |
emails.clicked_at | Email clicked date | ext, not_ext, lte, gte, range |
emails.bounced_at | Email bounced date | ext, not_ext, lte, gte, range |
emails.unsubscribed_at | Unsubscribed date | ext, not_ext, lte, gte, range |
emails.blacklisted_at | Blacklisted date | ext, not_ext, lte, gte, range |
emails.abuse_report_at | Abuse report date | ext, not_ext, lte, gte, range |
emails.clicked_urls | A URL that was clicked | ext, not_ext, eql, not_eql, contains, start_with |
Sanctions fields
sanctions.validity is a computed field — it does not store a date, it tells you whether a sanction is currently active: "ongoing" (started and not yet ended) or "ended" (end date is in the past).
| Attribute | Meaning | Operators |
|---|---|---|
sanctions.created_at | Sanction creation date | eql, not_eql, lte, gte, range |
sanctions.start_date | Sanction start date | eql, not_eql, lte, gte, range |
sanctions.end_date | Sanction end date | eql, not_eql, ext, not_ext, lte, gte, range |
sanctions.status | Sanction status | eql, not_eql, contains, not_contains |
sanctions.validity | "ongoing" or "ended" | eql |
Custom fields and form answers
These attributes use a different condition shape — you must also provideform_id and form_ref_ids:
| Attribute | What it searches | Operators |
|---|---|---|
custom_fields | Custom fields attached to contacts | ext, not_ext, eql, not_eql, contains, not_contains, lte, gte, range |
form | Survey, consent, level of support, presence, or task form answers | ext, not_ext, eql, not_eql, contains, not_contains, lte, gte, range |
form_id values from GET /v1/forms/type/{type} and form_ref_ids from GET /forms/{id}.
ext / not_ext only check whether the contact has filled in the given form_id — form_ref_ids is ignored. eql without a value checks that all listed form_ref_ids are present. eql with a value additionally checks that value matches the stored answer.
