CI Recipes
These recipes use the Management API to change flags from a pipeline. Each one is a GitHub Actions job and the same thing as a plain shell block, so it works in any CI system. They only need curl and jq, which ship on GitHub’s runners.
Before you start
Section titled “Before you start”- On a Teams or Enterprise plan, create a token in the console under Project > Tokens. Give it
writepermission for the recipes that change flags, and limit it to the environments the pipeline should touch. - Store the secret as
ROCKETFLAG_TOKENin your CI secret store. In GitHub that is Settings > Secrets and variables > Actions. - Find the flag id in the console, or look it up by name:
GET /api/v1/flags?name=my-flag.
The recipes assume a multi-environment project, so they pass ?env=production. On a single-environment project, drop the ?env= part. All of them read the token from ROCKETFLAG_TOKEN and use the flag id in FLAG_ID.
1. Enable a flag after a deploy
Section titled “1. Enable a flag after a deploy”Turn a flag on once the new version is live.
jobs: enable-flag: runs-on: ubuntu-latest needs: deploy steps: - name: Enable new-checkout in production env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' | jq '{changed, enabled: .flag.enabled}'The same thing in a shell:
set -euo pipefailexport ROCKETFLAG_TOKEN="rf_..."FLAG_ID="Gp9rS5dH1jXe6UaM0fTw"
curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' | jq '{changed, enabled: .flag.enabled}'--fail-with-body makes curl exit non-zero (status 22) on a 4xx or 5xx while still printing the error envelope, and set -euo pipefail makes the shell honour that status even though curl is piped into jq, so the job fails and the log says why.
2. Ramp up: 50% then 100%
Section titled “2. Ramp up: 50% then 100%”Two jobs, with your own health check in between. The second job only runs if the first, and the checks after it, pass.
jobs: ramp-50: runs-on: ubuntu-latest needs: deploy steps: - name: Roll out to 50% env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": true, "trafficPercentage": 50}' | jq .flag
verify: runs-on: ubuntu-latest needs: ramp-50 steps: - run: ./scripts/check-error-rate.sh # your own check
ramp-100: runs-on: ubuntu-latest needs: verify steps: - name: Roll out to 100% env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"trafficPercentage": 100}' | jq .flagIn a shell:
set -euo pipefailpatch() { curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d "$1" | jq .flag}
patch '{"enabled": true, "trafficPercentage": 50}'./scripts/check-error-rate.sh && patch '{"trafficPercentage": 100}'Percentage rollouts are sticky per user when your app sends a targetingKey, so users who were in at 50% stay in at 100%.
3. Kill switch on a failed smoke test
Section titled “3. Kill switch on a failed smoke test”If the post-deploy smoke test fails, switch the flag off. The if: failure() step runs only when an earlier step failed.
jobs: release: runs-on: ubuntu-latest steps: - name: Enable new-checkout env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' > /dev/null
- name: Smoke test run: ./scripts/smoke-test.sh
- name: Kill switch if: failure() env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": false}' | jq '{changed, enabled: .flag.enabled}'In a shell:
set -euo pipefailoff() { curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": false}' | jq '{changed, enabled: .flag.enabled}'}
./scripts/smoke-test.sh || { off; exit 1; }4. Gate a step on flag state
Section titled “4. Gate a step on flag state”Read the flag and only run a step when it is on, for example to run an expensive integration suite only while a feature is live. This needs only a read token.
jobs: integration: runs-on: ubuntu-latest steps: - name: Read flag state id: flag env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail enabled=$(curl -sS --fail-with-body \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" | jq -r '.enabled') echo "enabled=$enabled" >> "$GITHUB_OUTPUT"
- name: New checkout tests if: steps.flag.outputs.enabled == 'true' run: npm run test:new-checkoutIn a shell:
set -euo pipefailenabled=$(curl -sS --fail-with-body \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" | jq -r '.enabled')
if [ "$enabled" = "true" ]; then npm run test:new-checkoutfi5. Sync a cohort list from a file
Section titled “5. Sync a cohort list from a file”Keep a flag’s cohorts in version control. One cohort per line in cohorts.txt, and the pipeline makes the flag match it. jq turns the file into the JSON array.
beta-testersqa-teaminternal-staffon: push: branches: [main] paths: [cohorts.txt]
jobs: sync-cohorts: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Sync cohorts env: ROCKETFLAG_TOKEN: ${{ secrets.ROCKETFLAG_TOKEN }} FLAG_ID: Gp9rS5dH1jXe6UaM0fTw shell: bash run: | set -euo pipefail body=$(jq -Rn '{cohorts: [inputs | select(length > 0)]}' cohorts.txt) curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d "$body" | jq '{changed, cohorts: .flag.cohorts}'In a shell:
set -euo pipefailbody=$(jq -Rn '{cohorts: [inputs | select(length > 0)]}' cohorts.txt)
curl -sS --fail-with-body -X PATCH \ "https://api.rocketflag.app/api/v1/flags/$FLAG_ID?env=production" \ -H "Authorization: Bearer $ROCKETFLAG_TOKEN" \ -H "Content-Type: application/json" \ -d "$body" | jq '{changed, cohorts: .flag.cohorts}'A cohorts array replaces the list, it does not append, so the file is the single source of truth. On a multi-environment project, cohorts sets that environment’s override. An empty file removes the override, and the environment inherits the flag-wide list (see Patch). On a single-environment project, an empty file clears the list. Guard against an empty file if a missing file would be a mistake.
- Fail loudly. Keep
--fail-with-bodytogether withset -euo pipefail(orshell: bashin GitHub Actions). The error envelope’sdetailtells you what to fix. - Check the token first.
curl -sS -H "Authorization: Bearer $ROCKETFLAG_TOKEN" https://api.rocketflag.app/api/v1/token | jqprints the token’s permission, environments and expiry. - Never echo the secret. GitHub masks
secrets.ROCKETFLAG_TOKENin logs, but other CI systems may not. Do not run scripts withset -xwhile the token is in the environment. - Denials send emails. If a pipeline uses a token outside its permission or environments, the token’s creator and the organisation’s Owners are emailed, at most once per token per 24 hours. See Security.