CLI command
segments
Behavioural segments: the people whose stored events meet every rule of a segment, read live, with the history of who entered and left, and the lifecycle triggers that fire when someone enters.
Invocation#
echo-cli segments list --product <id>
echo-cli segments define --product <id> --name <text> --app <source> --file <rules.json> --by <name>
echo-cli segments members --segment <segment-id>
echo-cli segments refresh --segment <segment-id>
echo-cli segments entry --segment <segment-id>
echo-cli segments onentry --segment <segment-id> --trigger <trigger-id> --by <name>
echo-cli segments unentry --segment <segment-id> --trigger <trigger-id>
echo-cli segments remove --segment <segment-id>Inputs and output#
list writes the product's segments by name: id, product_id, name, app_id, rules, created_by and created_at; define and remove write the segment. members writes segment, members (the person keys in the segment now, sorted) and history (segment_id, person, entered_at and left_at, null while in), newest entry first. refresh writes segment_id, members, entered, left and enrolled (trigger_id, subject, email and enrollment_id for each person who entered, per trigger the segment fires on entry). entry writes the segment's bindings (segment_id, trigger_id, bound_by, bound_at), oldest first; onentry and unentry write the binding.
State effect#
Every verb calls Echo Web's authenticated /api/analytics/segments, /api/analytics/segments/refresh or /api/analytics/segments/entry with ECHO_INTERNAL_BEARER at ECHO_WEB_BASE_URL; the Behavioural segments section of the tracking screen and of Echo Desktop's Contact frequency screen call the same operations, and echo-mcp lists segments (echo_list_segments), reads members (echo_segment_members) and lists entry triggers (echo_segment_entry_triggers) but changes none. The --file holds the rules, [{ "event": "…", "within_days": <days>, "at_least": <times> }] or with "at_most": <times> instead (0 for never), at least one; a rule may add "where": { "property": "…", "equals": <value> } so only the sends whose property holds that value count — { "event": "plan_changed", "where": { "property": "plan", "equals": "pro" }, "within_days": 30, "at_least": 1 } — the value stored and compared as text, so 5 matches a stored "5" and a stored 5, and a send without the property does not count; a person is a member when every rule holds. members reads the source's stored web and mobile events over the longest window the rules name, joins every identifier the identity edges link into one person, counts each rule's event inside its own window, and answers at that moment; nothing is cached, so it always equals the rules over what is stored. Only people who sent some event of the source in that window are candidates. refresh, and the daily /api/cron/segments-refresh over every segment, compare the members now with the open entries recorded last and record who entered and who left; memberships are a personal store that privacy retention and erasure reach. A binding (segment_entry_triggers, made by onentry) makes the segment fire a lifecycle trigger of the same product and source on entry: every refresh enrols each person it records as entered in the trigger's sequence, as lifecycle_enrollments keeps it once per trigger and person, with enrolled_by_event segment:<segment id>; the person is named by the user id an identity edge links to them, else by the key the segment knows them by, with that user's email when Echo's sign-in knows it; unbinding keeps the enrolments made. A name is used once per product whatever its case; removing a segment removes its history.
Refusals#
- A wrong invocation is refused before anything is sent: "segments takes <every usage, separated by semicolons>; got <word>", "segments <verb> does not take <flag>; it takes …", "<flag> needs a value", "segments <verb> needs <flag>" for each flag the verb needs, and "--file <path> is not JSON: <parser's reason>"; a --file that cannot be read ends with the system's error naming the path.
- Echo Web refuses onentry with "trigger <id> belongs to product <id> and segment <id> to product <id>; a segment fires only its own product's triggers", "trigger <id> reads source <app> and segment <id> source <app>; a segment fires only triggers of its own source", "bound_by is required: an internal caller has no signed-in user, so it names who is acting" and, as a conflict, "segment <id> already fires trigger <id> on entry, bound by <name> at <time>"; not found with "no lifecycle trigger has id <id>; …" or, unbinding, "segment <id> does not fire trigger <id> on entry; echo-cli segments entry --segment <id> lists what it fires". Removing a bound segment is refused with "segment <id> fires the lifecycle triggers <ids> on entry; unbind them first with echo-cli segments unentry", and removing a bound trigger with "lifecycle trigger <id> fires on entering the segments <ids>; unbind it first with echo-cli segments unentry".
- Echo Web answers a bad request with "product_id is required: …", "name is required: …", "app_id is required: …", "rules must be [{ event, within_days, at_least, where? }] or [{ event, within_days, at_most, where? }], where being { property, equals }, at least one: <what is wrong>", "segment_id is required: …", "product_id or segment_id is required: …", "created_by is required: an internal caller has no signed-in user, so it names who is acting" or, removing a segment a win-back rule reads its inactivity from, "segment <id> is the inactivity of the win-back rules <name> (<id>); remove them first with echo-cli lifecycle winback remove"; not found with "no product has id <id>; echo-cli products lists the ones that exist" or "no segment has id <id>; echo-cli segments list --product <id> lists them"; a conflict with "product <id> already has a segment named <name>, <id>". A refresh that cannot write its history fails with "the people who entered segment <id> could not be recorded: <cause>" (or left).