Skip to content
Shoal
Shoal

Core concepts

Targeting rules

Serve different variations based on user attributes, segments and percentages.

Targeting decides who gets which variation. You pass a context with every evaluation, and rules match against its attributes.

The evaluation context

context.ts
const ctx = {
  user: {
    id: "u_42",            // required, used for stable bucketing
    email: "mia@acme.dev",
    plan: "pro",
    country: "KR",
    createdAt: "2026-03-14",
  },
  device: { os: "ios", appVersion: "5.2.0" },
};

Any attribute can be targeted. Nested objects are addressed with dots, e.g. device.appVersion.

Rules

A rule is a list of conditions (all must match) and a result — a variation or a percentage rollout.

rule.json
{
  "description": "Pro users in Korea",
  "conditions": [
    { "attribute": "user.plan", "op": "in", "values": ["pro", "team"] },
    { "attribute": "user.country", "op": "eq", "values": ["KR"] },
    { "attribute": "device.appVersion", "op": "semver_gte", "values": ["5.0.0"] }
  ],
  "serve": { "variation": "on" }
}

Operators

  • eq, neq, in, not_in — exact matches
  • contains, starts_with, ends_with, matches (regex) — strings
  • gt, gte, lt, lte — numbers and ISO dates
  • semver_eq, semver_gte, semver_lt — version strings
  • in_segment — membership in a saved segment

Segments

A segment is a reusable rule — “Beta testers”, “Internal staff”, “Enterprise accounts”. Update the segment once and every flag that targets it follows.

Percentage rollouts

Instead of a single variation, a rule can split traffic. Shoal hashes flagKey + user.id so the same user always lands in the same bucket, across servers and sessions.

Edit this pageLast updated:
Was this page helpful?

Type to search. Results come from a local index — nothing leaves your browser.

↑ ↓ to navigate↵ to openMiniSearch · local