curl --request POST \
--url https://galinum.com/api/v1/audiences/explain \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expression": {
"version": 1,
"root": {
"kind": "all",
"children": [
{
"kind": "field",
"field": {
"kind": "trait",
"key": "plan"
},
"op": "eq",
"value": "free"
},
{
"kind": "not",
"child": {
"kind": "event",
"event": "used_csv_export"
}
}
]
}
},
"userId": "<string>",
"segment": "<string>",
"version": 2
}
'import requests
url = "https://galinum.com/api/v1/audiences/explain"
payload = {
"expression": {
"version": 1,
"root": {
"kind": "all",
"children": [
{
"kind": "field",
"field": {
"kind": "trait",
"key": "plan"
},
"op": "eq",
"value": "free"
},
{
"kind": "not",
"child": {
"kind": "event",
"event": "used_csv_export"
}
}
]
}
},
"userId": "<string>",
"segment": "<string>",
"version": 2
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
expression: {
version: 1,
root: {
kind: 'all',
children: [
{kind: 'field', field: {kind: 'trait', key: 'plan'}, op: 'eq', value: 'free'},
{kind: 'not', child: {kind: 'event', event: 'used_csv_export'}}
]
}
},
userId: '<string>',
segment: '<string>',
version: 2
})
};
fetch('https://galinum.com/api/v1/audiences/explain', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://galinum.com/api/v1/audiences/explain",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'expression' => [
'version' => 1,
'root' => [
'kind' => 'all',
'children' => [
[
'kind' => 'field',
'field' => [
'kind' => 'trait',
'key' => 'plan'
],
'op' => 'eq',
'value' => 'free'
],
[
'kind' => 'not',
'child' => [
'kind' => 'event',
'event' => 'used_csv_export'
]
]
]
]
],
'userId' => '<string>',
'segment' => '<string>',
'version' => 2
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://galinum.com/api/v1/audiences/explain"
payload := strings.NewReader("{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://galinum.com/api/v1/audiences/explain")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://galinum.com/api/v1/audiences/explain")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}"
response = http.request(request)
puts response.read_body{
"user": {
"externalUserId": "<string>",
"firstSeenAt": 123,
"lastSeenAt": 123
},
"matched": true,
"evaluatedAt": 123,
"expressionHash": "<string>",
"trace": {
"path": "<string>",
"kind": "<string>",
"matched": true,
"field": {
"kind": "trait",
"key": "<string>"
},
"op": "<string>",
"observed": "<unknown>",
"observedType": "<string>",
"note": "<string>",
"event": "<string>",
"occurrences": 123,
"required": {},
"windowCutoff": 123,
"children": [
"<unknown>"
],
"child": {}
},
"segment": {
"id": "<string>",
"key": "<string>",
"version": 123
}
}{
"error": "<string>",
"diagnostics": [
{
"severity": "error",
"code": "unobserved_event",
"path": "/root/children/1",
"message": "<string>",
"suggestions": [
"<string>"
]
}
]
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Explain an audience for one user
Evaluates an audience expression for ONE project user and returns a condition-level evidence trace mirroring the expression tree — the debugging surface for “why did this user match?” and “why did this user not match?”. Each trace node reports its JSON Pointer path, matched state, and the compact evidence behind the decision: observed value and type for field conditions, occurrence counts and window boundaries for event conditions, and per-child outcomes for groups. Uses the same evaluator as check and message eligibility, so the three always agree for the same user, expression, and evaluatedAt.
curl --request POST \
--url https://galinum.com/api/v1/audiences/explain \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expression": {
"version": 1,
"root": {
"kind": "all",
"children": [
{
"kind": "field",
"field": {
"kind": "trait",
"key": "plan"
},
"op": "eq",
"value": "free"
},
{
"kind": "not",
"child": {
"kind": "event",
"event": "used_csv_export"
}
}
]
}
},
"userId": "<string>",
"segment": "<string>",
"version": 2
}
'import requests
url = "https://galinum.com/api/v1/audiences/explain"
payload = {
"expression": {
"version": 1,
"root": {
"kind": "all",
"children": [
{
"kind": "field",
"field": {
"kind": "trait",
"key": "plan"
},
"op": "eq",
"value": "free"
},
{
"kind": "not",
"child": {
"kind": "event",
"event": "used_csv_export"
}
}
]
}
},
"userId": "<string>",
"segment": "<string>",
"version": 2
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
expression: {
version: 1,
root: {
kind: 'all',
children: [
{kind: 'field', field: {kind: 'trait', key: 'plan'}, op: 'eq', value: 'free'},
{kind: 'not', child: {kind: 'event', event: 'used_csv_export'}}
]
}
},
userId: '<string>',
segment: '<string>',
version: 2
})
};
fetch('https://galinum.com/api/v1/audiences/explain', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://galinum.com/api/v1/audiences/explain",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'expression' => [
'version' => 1,
'root' => [
'kind' => 'all',
'children' => [
[
'kind' => 'field',
'field' => [
'kind' => 'trait',
'key' => 'plan'
],
'op' => 'eq',
'value' => 'free'
],
[
'kind' => 'not',
'child' => [
'kind' => 'event',
'event' => 'used_csv_export'
]
]
]
]
],
'userId' => '<string>',
'segment' => '<string>',
'version' => 2
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://galinum.com/api/v1/audiences/explain"
payload := strings.NewReader("{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://galinum.com/api/v1/audiences/explain")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://galinum.com/api/v1/audiences/explain")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"expression\": {\n \"version\": 1,\n \"root\": {\n \"kind\": \"all\",\n \"children\": [\n {\n \"kind\": \"field\",\n \"field\": {\n \"kind\": \"trait\",\n \"key\": \"plan\"\n },\n \"op\": \"eq\",\n \"value\": \"free\"\n },\n {\n \"kind\": \"not\",\n \"child\": {\n \"kind\": \"event\",\n \"event\": \"used_csv_export\"\n }\n }\n ]\n }\n },\n \"userId\": \"<string>\",\n \"segment\": \"<string>\",\n \"version\": 2\n}"
response = http.request(request)
puts response.read_body{
"user": {
"externalUserId": "<string>",
"firstSeenAt": 123,
"lastSeenAt": 123
},
"matched": true,
"evaluatedAt": 123,
"expressionHash": "<string>",
"trace": {
"path": "<string>",
"kind": "<string>",
"matched": true,
"field": {
"kind": "trait",
"key": "<string>"
},
"op": "<string>",
"observed": "<unknown>",
"observedType": "<string>",
"note": "<string>",
"event": "<string>",
"occurrences": 123,
"required": {},
"windowCutoff": 123,
"children": [
"<unknown>"
],
"child": {}
},
"segment": {
"id": "<string>",
"key": "<string>",
"version": 123
}
}{
"error": "<string>",
"diagnostics": [
{
"severity": "error",
"code": "unobserved_event",
"path": "/root/children/1",
"message": "<string>",
"suggestions": [
"<string>"
]
}
]
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Your project's secret key (pk_…, without the pub segment). Server-side only — never ship it to a browser. Get it from Settings → Projects in the dashboard.
Body
- Option 1
- Option 2
Provide exactly one of expression (inline) or segment (+ optional version) — both run through the identical validator and evaluator.
Versioned, strictly validated audience rule. Example: { "version": 1, "root": { "kind": "all", "children": [ { "kind": "field", "field": { "kind": "trait", "key": "plan" }, "op": "eq", "value": "free" }, { "kind": "not", "child": { "kind": "event", "event": "used_csv_export" } } ] } }
Show child attributes
Show child attributes
{
"version": 1,
"root": {
"kind": "all",
"children": [
{
"kind": "field",
"field": { "kind": "trait", "key": "plan" },
"op": "eq",
"value": "free"
},
{
"kind": "not",
"child": {
"kind": "event",
"event": "used_csv_export"
}
}
]
}
}
The Galinum user id (eu_…) or your own external user id.
Saved segment to evaluate, by id (seg_…) or machine key. Provide exactly one of expression or segment.
Segment version to evaluate; omitted = current.
x >= 1Response
Evaluation trace.
Show child attributes
Show child attributes
Trace node mirroring the expression tree. Every node carries path (JSON Pointer into the expression), kind, and matched. Field nodes add field, op, observed (compact evidence — long strings truncated, objects reported by type), observedType, and a note for absence/type mismatches. Event nodes add event, occurrences, required count, and windowCutoff. Group nodes carry children; not carries child.
Show child attributes
Show child attributes
Present when the evaluated audience came from a saved segment.
Show child attributes
Show child attributes