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
Date fields accept relative values:
"now", "now-30d", "now-1y", "now+1y". All dates are interpreted as UTC.
Pagination and sorting
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
Address fields
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.*)
Donations (
donations.*)
Memberships (
memberships.*)
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.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).
Custom fields and form answers
These attributes use a different condition shape — you must also provideform_id and form_ref_ids:
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.
