This tutorial schedules a rollout that starts at 10% exposure and increases from there, explains the bucketing math that keeps a given user's outcome stable across stages, and shows how to intervene if a stage doesn't look right.
1. Schedule a rollout starting at 10%
A rollout is attached to an environment's flag config, not to the flag itself, so create it with POST /v1/environment-flag-configs/{environment_flag_config_id}/rollouts. environment_flag_config_id is the id returned when you PUT the flag's config for that environment (see "Creating a feature flag"). The config must have no manually-authored rules and no other scheduled or active rollout when you create this one.
A rollout is a list of stages, each with a target_percentage and an activate_at time. Percentages must strictly increase and the last stage must reach 100:
const res = await fetch(
`https://api.rollfuse.com/v1/environment-flag-configs/${environmentFlagConfigId}/rollouts`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
target_variation_id: onVariationId,
stages: [
{ target_percentage: 10, activate_at: "2026-09-06T12:00:00Z" },
{ target_percentage: 50, activate_at: "2026-09-08T12:00:00Z" },
{ target_percentage: 100, activate_at: "2026-09-10T12:00:00Z" },
],
}),
}
);
const rollout = await res.json();
console.log(rollout.id, rollout.state); // "scheduled"
The rollout starts in the scheduled state and becomes active as its stages fire. You can check its current state, and which stage was last applied, with GET /v1/rollouts/{rollout_id}:
const check = await fetch(`https://api.rollfuse.com/v1/rollouts/${rollout.id}`, {
headers: { Authorization: `Bearer ${token}` },
});
const current = await check.json();
console.log(current.state, current.last_applied_stage_position);
2. Why the same user stays on the same side as the percentage rises
@rollfuse/sdk-js exports a bucket() function that is the same deterministic function the platform uses to decide who's included at a given rollout percentage:
import { bucket, BUCKET_MODULUS } from "@rollfuse/sdk-js";
const b = bucket("new-checkout-flow", "user-42");
console.log(b, BUCKET_MODULUS); // e.g. 3821 10000
bucket(flagKey, subjectKey) hashes flagKey + ":" + subjectKey with FNV-1a and reduces it modulo BUCKET_MODULUS (10000), giving 0.01% granularity. A stage at 10% includes every subject whose bucket value is below 1000 out of 10000. Because the hash depends only on the flag key and the subject key, a given user's bucket value never changes for that flag across stages. That's what keeps a rollout from flip-flopping users in and out as it advances: going from a 10% stage to a 50% stage only ever adds users, it never removes anyone already included.
function isIncludedAtPercentage(flagKey, subjectKey, percentage) {
return bucket(flagKey, subjectKey) < (percentage / 100) * BUCKET_MODULUS;
}
isIncludedAtPercentage("new-checkout-flow", "user-42", 10); // false, if b = 3821
isIncludedAtPercentage("new-checkout-flow", "user-42", 50); // true, since 3821 < 5000
3. Intervene mid-rollout if something looks wrong
You don't edit a stage's percentage after the fact; instead, control the rollout's lifecycle directly:
POST /v1/rollouts/{rollout_id}/pausefreezes it at its current percentage.POST /v1/rollouts/{rollout_id}/resumecontinues a paused rollout.POST /v1/rollouts/{rollout_id}/abortends it before it reaches 100%.
await fetch(`https://api.rollfuse.com/v1/rollouts/${rollout.id}/pause`, {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
});
If you want this to happen automatically instead of by hand, see the automatic guardrail rollback tutorial: attaching a guardrail to this same rollout lets a breaching metric pause or abort it for you.
4. Confirm it from application code
Evaluation from @rollfuse/sdk-js reflects the rollout's current stage automatically; you don't compute buckets yourself in application code, only when you want to reason about or test the rollout's behavior offline:
const result = client.evaluate("user-42", "new-checkout-flow", {
attributes: { plan: "pro" },
fallback: false,
});
console.log(result.value, result.reason);
Where to go next
A staged rollout treats every subject the same at each percentage. If you need to target a specific group first, for example enterprise customers before everyone else, that's a targeting rule on the flag config, covered in the targeting-with-segments tutorial, since a config with manual rules can't also carry a percentage rollout.