Amba

Segment Targeting

Store per-segment and percentage-rollout conditions on remote-config keys.

A remote config key has a default value and an ordered list of conditions. Each condition is either a segment override (segment_id) or a percentage rollout (percentage of users). The admin API stores the conditions with the key.

GET /v1/client/config evaluates the conditions for a signed-in user. The first condition that applies supplies the value: a segment override applies when the user is in the segment, a percentage rollout when the user's bucket for that key falls under the percentage. A user no condition matches gets the default value, and so does every caller without a session (an app fetching config before sign-in, or with an expired session token).

Condition shape

type Condition = { segment_id: string; value: unknown } | { percentage: number; value: unknown }; // 0–100

Conditions live in remote_configs.conditions (JSONB array), in the order you list them.

Segment override example

Store an override of true for the power_users segment on a key that defaults to false:

POST /v1/admin/projects/:projectId/config
 
{
  "key": "feature_paywall_v2",
  "value": false,
  "value_type": "boolean",
  "description": "New paywall flow",
  "conditions": [
    { "segment_id": "seg_power_users", "value": true }
  ]
}

GET /v1/client/config returns feature_paywall_v2: true to signed-in members of seg_power_users and false to everyone else. Segment membership is what the latest segment evaluation recorded; amba_segments_evaluate refreshes it now.

Percentage rollout example

Turn the key on for 25% of signed-in users. Each user lands in a stable bucket per key (sha256(user_id | key) % 100), so a user keeps the same answer across fetches:

POST /v1/admin/projects/:projectId/config
 
{
  "key": "feature_onboarding_v2",
  "value": false,
  "value_type": "boolean",
  "conditions": [
    { "percentage": 25, "value": true }
  ]
}

Combining overrides and rollout

A key can hold several conditions. They are checked in the order you list them and the first match wins, so internal testers always get the new UI and 10% of everyone else does:

{
  "key": "feature_new_ui",
  "value": false,
  "conditions": [
    { "segment_id": "seg_internal_testers", "value": true },
    { "percentage": 10, "value": true }
  ]
}

Multi-value overrides

Different cohorts, different values. value_type must match across conditions:

{
  "key": "weekly_goal_count",
  "value_type": "number",
  "value": 5,
  "conditions": [
    { "segment_id": "seg_new_users", "value": 3 },
    { "segment_id": "seg_power_users", "value": 10 }
  ]
}

From the SDK

The SDK reads the flat key → value map, evaluated for the signed-in user. Fetch after sign-in to get the targeted values; before sign-in the map holds each key's default value:

const { values } = await client.config.fetch();
const goal = (values['weekly_goal_count'] as number | undefined) ?? 5;

Invalidation

Any mutation to a config row bumps the singleton config_versions.version_hash, which is the ETag on GET /v1/client/config. A signed-in user's ETag also carries the user's id and a hash of the user's segment memberships, so joining or leaving a segment changes it. A client that sends the saved ETag back in If-None-Match gets 304 while nothing changed. Adding or changing conditions also changes the ETag, since version_hash includes conditions in its input.

Next

On this page