Segments
A segment is a named group of users. It is one of two kinds, chosen when you create it and never changed:
- Rules (
kind: rules, the default): the users whose attributes match conditions you set, such as plan isenterprise. The rule format and its limits are in the API reference. - ID list (
kind: id_list): the users whoseuser_idyou upload, for example a customer export. This page is about these.
The member routes are beta (x-stability: beta): their shape may still change. The full
contract and every refusal are in the
API reference.
The examples run against the Quick Start stack and its demo data.
Sign in
Changing a segment takes the ADMIN or DEVELOPER role. Sign in as the demo administrator:
TOKEN=$(curl -s -X POST localhost:8000/api/v1/auth/login \
-H 'content-type: application/json' \
-d '{"email":"admin@demo.com","password":"Demo1234!"}' | jq -r .access_token)
curl -s localhost:8000/api/v1/auth/me -H "Authorization: Bearer $TOKEN" | jq .role
It prints "ADMIN".
Create an ID-list segment
An ID-list segment has no rules. This creates one and saves its id in $SEGMENT:
SEGMENT=$(curl -s -X POST localhost:8000/api/v1/segments \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"name": "Enterprise pilot", "kind": "id_list"}' | jq -r .id)
curl -s localhost:8000/api/v1/segments/$SEGMENT \
-H "Authorization: Bearer $TOKEN" | jq -c '{kind, rules, member_count}'
It prints {"kind":"id_list","rules":null,"member_count":0}.
Add IDs
Send up to 10,000 IDs per request. Each is 1 to 255 characters and is matched exactly: not trimmed, not case-folded.
curl -s -X POST localhost:8000/api/v1/segments/$SEGMENT/members \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"add": ["cust-001", "cust-002", "cust-003"]}' | jq -c .
It prints {"added":3,"already_members":0,"member_count":3}.
Sending the same IDs again is safe. An ID already in the segment is counted in
already_members and left alone, so after a failure you can send the whole chunk again:
curl -s -X POST localhost:8000/api/v1/segments/$SEGMENT/members \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"add": ["cust-003", "cust-004"]}' | jq -c .
It prints {"added":1,"already_members":1,"member_count":4}.
Add IDs from a file
A segment holds at most 1,000,000 IDs. Send a longer file in chunks of 10,000, one request
at a time, and stop at the first error. This writes 25,000 IDs, one per line, to a file in a
scratch directory, sends them in three chunks and prints each answer. Each body is piped to
curl (--data-binary @-): 10,000 IDs are too long for one command-line argument on Linux.
cd "$(mktemp -d)"
seq -f 'cust-%06g' 1 25000 > customers.txt
split -l 10000 customers.txt chunk-
for chunk in chunk-*; do
RESPONSE=$(jq -R . "$chunk" | jq -s '{add: .}' | curl -s -f -X POST localhost:8000/api/v1/segments/$SEGMENT/members \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
--data-binary @-) || break
jq -c . <<<"$RESPONSE"
done
It prints one line per chunk. The last is {"added":5000,"already_members":0,"member_count":25004}.
The file's IDs are written cust-000001 to cust-025000, so none of them was in the segment
before (cust-001 is a different ID).
A request over a limit answers 422 and adds nothing. The message names the field and the
position, never an ID: add: at most 10,000 IDs per request, add[17]: an ID is 1 to 255
characters, or, past the cap, this segment would have 1,000,250 members; a segment holds
at most 1,000,000.
Check a user
A user is a member when the user_id in the context you send is in the list:
curl -s -X POST localhost:8000/api/v1/segments/$SEGMENT/evaluate \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"user_context": {"user_id": "cust-002"}}' | jq .is_member
It prints true. A user_id that is not in the list, or a context with no user_id, prints
false.
Remove IDs
curl -s -X POST localhost:8000/api/v1/segments/$SEGMENT/members/remove \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"remove": ["cust-002", "cust-999"]}' | jq -c .
It prints {"removed":1,"not_members":1,"member_count":25003}: cust-999 was not a member.
Target a flag or an experiment at a segment
A targeting condition names the segment by id: {"attribute": "segment", "operator":
"in_segment", "value": "<segment id>"}, or not_in_segment for everyone else. This creates
a flag that is on only for the segment's members (its rollout outside the segment is 0%):
FLAG=pilot-banner-$(date +%s)
jq -n --arg key "$FLAG" --arg segment "$SEGMENT" '{
key: $key, name: "Pilot banner", is_active: true, rollout_percentage: 0,
targeting_rules: {groups: [{conditions: [
{attribute: "segment", operator: "in_segment", value: $segment}]}]}}' \
| curl -s -X POST localhost:8000/api/v1/feature-flags/ \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
--data-binary @- | jq -r .status
It prints active. Evaluating it takes an API key. cust-001 is in the list and cust-999 is
not:
KEY=$(curl -s -X POST localhost:8000/api/v1/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"name": "Segments guide"}' | jq -r .key)
for USER in cust-001 cust-999; do
printf '%s ' "$USER"
curl -s -X POST localhost:8000/api/v1/feature-flags/evaluate/$FLAG \
-H "X-API-Key: $KEY" \
-H 'content-type: application/json' \
-d "{\"user_id\": \"$USER\"}" | jq -c '{enabled, reason}'
done
It prints one line per user: cust-001 gets {"enabled":true,"reason":"targeting_rule"}, and
cust-999 falls through to the 0% rollout, {"enabled":false,"reason":"rollout"}.
An experiment takes the same condition in its targeting_rules; a user who is not a member
gets the control variant with assigned: false and reason: "targeting".
- The server decides membership. For an ID list, the user is the
user_idthe flag or the assignment is evaluated for; auser_id(or anything else) in the context you send does not make a user a member. - At most 10 segments per ruleset, one per condition; put several conditions in an
ORgroup to match any of them. - Saving rules that name a segment that is unknown, inactive or archived answers
422. If the server cannot decide membership when the flag is evaluated (the flag was unarchived after its segment was archived, say), the flag answers{"enabled": false, "reason": "error"}and the experiment does not enrol the user, forin_segmentandnot_in_segmentalike. - SDKs that evaluate flags locally ask the server about a flag that uses a segment: see Local evaluation.
The full rule is in the API reference.
Archive a segment
A segment that a flag or an experiment still uses cannot be archived or made inactive: it
answers 409 and lists them. A flag counts until it is archived, and an experiment until it is
completed or archived, because until then its rules can still be evaluated.
curl -s -X DELETE localhost:8000/api/v1/segments/$SEGMENT \
-H "Authorization: Bearer $TOKEN" | jq -c '{code: .detail.code, flags: (.detail.feature_flags | length)}'
It prints {"code":"segment_in_use","flags":1}. The full answer is
{"detail": {"code": "segment_in_use", "message", "feature_flags": [{"id", "key", "name"}],
"experiments": [{"id", "key", "name", "status"}]}}. Remove the segment from those rules, or
archive the flag, then archive the segment.
Limits
| Limit | Value |
|---|---|
| IDs per request | 1 to 10,000 |
| Characters per ID | 1 to 255 |
| IDs per segment | 1,000,000 |
| Who can change members | ADMIN and DEVELOPER (ANALYST and VIEWER get 403) |
| Segments per targeting ruleset | 10 |
A request body is at most 5 MiB (5,242,880 bytes); a larger one is answered 413. 10,000 IDs
of 255 ASCII characters come to about 2.6 MB, but the ID limit counts characters, not bytes: with
long non-ASCII IDs, send fewer per request.
Each add or remove writes one segment_update entry to the audit log with the counts, never
the IDs. A rules segment answers 409 on the member routes, and so does an archived one.
Before you roll a database back past the release that added ID lists, read
37dcb2969766:
its downgrade deletes every list's members, so it refuses while any list has members.