Assumptions
- Segment IDs are Kevel segment IDs. No GAM→Kevel ID mapping is performed. A criterion referencing segment
3translates directly to$user.segments contains 3. - The segment catalog at /segments is a demo set, standing in for segments assumed to already exist in the Kevel network. When integrating for real, replace it with a live lookup against the Kevel Management API. Unknown segment IDs still translate (with a warning) — IDs are passed through as-is.
- Only
IS/IS_NOToperators are supported, mirroring GAM custom targeting.
Endpoints
GET/
Interactive playground — visual rule builder with live Zerkel output.
GET/segments
Demo segment catalog. These segments are assumed to already exist in the Kevel network.
{
"segments": [
{ "id": 3, "name": "People who like Mexico" },
{ "id": 7, "name": "Frequent Travelers" },
{ "id": 12, "name": "Travel Intent — Europe" },
...
]
}
POST/translate/gam
Translate GAM custom targeting to a Kevel CustomTargeting Zerkel string.
Input A — Structured (primary, for Advendio integration)
| Field | Type | Description | |
|---|---|---|---|
| groups | required | array | Array of targeting groups. Multiple groups are joined by groupOperator. |
| groups[].criteria | required | array | Criteria within this group, joined by groups[].operator. Each criterion is either key/value or segment (see below). |
| groups[].operator | optional | string | How criteria inside this group are joined: AND (default) or OR. |
| groupOperator | optional | string | How groups are joined: OR (default) or AND. |
Key/value criterion
| Field | Type | Description | |
|---|---|---|---|
| key | required | string | Targeting key name. Hyphens are converted to underscores. |
| op | optional | string | IS (default) or IS_NOT. Also accepts operator as alias. |
| values | optional | string[] | Target values. Multiple values within a criterion are joined with OR. |
Segment criterion
| Field | Type | Description | |
|---|---|---|---|
| type | required | string | Must be "segment". (Alternatively, presence of segmentIds is enough.) |
| op | optional | string | IS (default) or IS_NOT. |
| segmentIds | required | number[] | Kevel segment IDs, emitted as-is. Multiple IDs are joined with OR. |
{
"groups": [
{
"criteria": [
{ "key": "category", "op": "IS", "values": ["shoes", "boots"] },
{ "type": "segment", "op": "IS", "segmentIds": [3, 7] }
]
}
],
"groupOperator": "OR"
}
Input B — String (secondary, for quick testing)
| Field | Type | Description | |
|---|---|---|---|
| gamTargeting | required | string | GAM-style targeting string: key=v1,v2 AND key!=v3. The reserved key segment (also segments / $user.segments) targets segments by ID: segment=3,7. |
{ "gamTargeting": "category=shoes,boots AND segment=3,7" }
Response 200
{
"zerkel": "\"(category contains \\\"shoes\\\" or category contains \\\"boots\\\") and ($user.segments contains 3 or $user.segments contains 7)\"",
"warnings": []
}
Note on escaping: The
zerkel value is a JSON string. The actual expression saved to Kevel's CustomTargeting field includes the outer quotes with inner quotes escaped.
GET/health
Health check.
{ "ok": true }
Translation Logic
Key/value operators
| GAM op | Values | Zerkel output |
|---|---|---|
| IS | one | key contains "value" |
| IS | many | (key contains "v1" or key contains "v2") |
| IS_NOT | one | not (key contains "value") |
| IS_NOT | many | not (key contains "v1" or key contains "v2") |
Segment operators
| Op | IDs | Zerkel output |
|---|---|---|
| IS | one | $user.segments contains 3 |
| IS | many | ($user.segments contains 3 or $user.segments contains 7) |
| IS_NOT | one | not ($user.segments contains 3) |
| IS_NOT | many | not ($user.segments contains 3 or $user.segments contains 7) |
Grouping
- Criteria within a group are joined with
andby default. - Multiple groups are joined with
orby default (same as GAM). - When there are multiple groups, each group is wrapped in parentheses.
- Key names with hyphens (
page-type) are normalised to underscores (page_type).
Warnings
The response always includes a warnings array. Non-fatal issues (empty groups, missing keys, unknown operators, segment IDs not in the demo catalog) are reported there rather than returning a 400 — the valid criteria are still translated.
Quick-test with curl
# Structured with a segment criterion
curl -X POST https://ct.kevel.me/translate/gam \\
-H "Content-Type: application/json" \\
-d '{
"groups": [{
"criteria": [
{ "key": "category", "op": "IS", "values": ["shoes"] },
{ "type": "segment", "op": "IS", "segmentIds": [3, 7] }
]
}]
}'
# String with the reserved segment key
curl -X POST https://ct.kevel.me/translate/gam \\
-H "Content-Type: application/json" \\
-d '{ "gamTargeting": "category=shoes AND segment=3,7" }'