---
title: "Update a tier"
method: PATCH
path: "/v1/programs/{program_id}/tiers/{tier_key}"
tags: ["Tiers"]
---

# Update a tier

`PATCH /v1/programs/{program_id}/tiers/{tier_key}`

## Request

- Base URL: `https://api.scrip.dev`
- URL: `https://api.scrip.dev/v1/programs/{program_id}/tiers/{tier_key}`
- Auth: HTTP bearer

Update a tier or its levels, or archive or restore the tier by setting `status`.

## Path parameters

- `program_id` string, uuid, required
- `tier_key` string, required

## Request body

- UpdateTierRequest: Changes a tier's name, status, lifecycle or levels.
  - `display_name` string: Human-readable name shown in dashboards and reports
  - `levels` UpdateTierLevelInput[]: Levels to upsert. Existing levels (matched by `key`) are updated; new keys create new levels.
    - `benefits` object: Arbitrary key-value payload granted when a participant reaches this level
    - `color` string: Hex color code for UI display
    - `display_name` string: The name people see for the level, such as `Gold`.
    - `icon_url` string: URL to an icon image for this level
    - `key` string, required: Level key to update (or create if it doesn't exist)
    - `qualification` TierQualification: Counter-threshold criteria for automatic advancement into a tier level.
      - `criteria` TierQualificationCriterion[]: Non-empty list of counter-threshold criteria. Required for a non-empty qualification; omitted for the "{}" clear value.
        - `counter` string, required: Counter key compared against the threshold.
        - `operator` '>=' | '>' | '==' | '<=' | '<', required: Comparison operator.
        - `threshold` number: Threshold value. Must be >= 0. Defaults to 0 when omitted.
      - `mode` 'ANY' | 'ALL': Match mode. ANY matches when any criterion is met; ALL requires every one. Required for a non-empty qualification; omitted for the "{}" clear value.
    - `qualification_condition` string: A shorter way to write `qualification`: comparisons of `participant.counter.<key>` or `participant.counter["<key>"]` with a number, joined with `&&` or `||`, such as `participant.counter.nights >= 25`. Scrip turns it into `qualification` when you save the tier, and checks it against the counters after the event's actions run, not against the event-start snapshot that rule conditions read. Do not send it with `qualification`.
    - `rank` integer: Position in the progression order (higher = better, required when creating a new level)
  - `lifecycle` TierLifecycle: Time-based tier automation: retention, qualification period, downgrade policy, and counter rollover.
    - `counters` TierCounters: The qualifying counters, and what a `tier_evaluation` does to them.
      - `qualifying` string[]: Counter keys that count toward qualification.
      - `rollover` 'NONE' | 'EXCESS': What a `tier_evaluation` does to qualifying counters. NONE resets them to 0; EXCESS carries the amount above the qualifying threshold into the next period.
    - `downgrade_policy` TierDowngradePolicy: Target level when a participant no longer qualifies.
      - `grace_days` integer: Days to defer a downgrade; the current level is held until grace end.
      - `min_level` string: Level key the participant is never downgraded below.
      - `mode` 'DROP_TO_QUALIFYING' | 'DROP_ONE' | 'HOLD', required: Downgrade mode. DROP_TO_QUALIFYING drops to the highest still-qualifying level; DROP_ONE drops one rank; HOLD keeps the current level.
    - `qualification_period` TierQualificationPeriod: The period that `status_validity` counts from when a `tier_evaluation` runs.
      - `start_day` integer: Day the period starts (1-31). Defaults to 1.
      - `start_month` integer: Month the period starts (1-12). Required when type is FIXED_YEAR.
      - `type` 'CALENDAR_YEAR' | 'FIXED_YEAR' | 'NONE', required: Period type.
    - `retention` TierRetention: How a participant keeps or loses tier status over time.
      - `duration` string: Duration string with w, d, h, m or s units. Required when mode is ACTIVITY_REFRESH; ignored for PERIOD_BASED. Examples: "90d", "1w", or "2160h".
      - `mode` 'PERIOD_BASED' | 'ACTIVITY_REFRESH', required: Retention mode. PERIOD_BASED keeps a level until a `tier_evaluation` re-evaluates it at the end of the qualification period. Scrip schedules that evaluation for a program's tier whose `qualification_period.type` is CALENDAR_YEAR or FIXED_YEAR. It runs at 00:00 UTC on the start date of each new period, for each ACTIVE participant enrolled in the program. Do not add your own cron for it. A NONE period has no end, so Scrip does not schedule one. ACTIVITY_REFRESH extends expiry on qualifying activity.
    - `status_validity` TierStatusValidity: How long a status grant remains valid past the period end.
      - `extend_months` integer: Calendar months added to the end of the period that contains a `tier_evaluation`. The result becomes the `expires_at` of the level kept or assigned. Send 0 or leave it out for no end date.
  - `status` 'ACTIVE' | 'ARCHIVED': Set to `ARCHIVED` to archive the tier, or back to `ACTIVE` to restore it; send `status` without other fields.

## Response `200`

Updated tier

- TierResponse: A tier definition with its levels, lifecycle configuration, and metadata
  - `archived_at` string, date-time: When the tier was archived (RFC 3339); absent while the tier is ACTIVE
  - `created_at` string, date-time: When this tier was created (RFC 3339)
  - `display_name` string: The name people see for the tier, such as `Loyalty status`.
  - `id` string, uuid: Unique identifier for this tier
  - `key` string: Tier key used in API calls and rule actions
  - `levels` TierLevelResponse[]: Ordered list of levels within this tier
    - `benefits` object: Key-value payload granted when a participant reaches this level
    - `color` string: Hex color code for UI display
    - `created_at` string, date-time: When this level was created (RFC 3339)
    - `display_name` string: The name people see for the level, such as `Gold`.
    - `icon_url` string: URL to an icon image for this level
    - `id` string, uuid: Unique identifier for this level
    - `key` string: Level key used in API calls and rule actions
    - `qualification` TierQualification: Counter-threshold criteria for automatic advancement into a tier level.
      - `criteria` TierQualificationCriterion[]: Non-empty list of counter-threshold criteria. Required for a non-empty qualification; omitted for the "{}" clear value.
        - `counter` string, required: Counter key compared against the threshold.
        - `operator` '>=' | '>' | '==' | '<=' | '<', required: Comparison operator.
        - `threshold` number: Threshold value. Must be >= 0. Defaults to 0 when omitted.
      - `mode` 'ANY' | 'ALL': Match mode. ANY matches when any criterion is met; ALL requires every one. Required for a non-empty qualification; omitted for the "{}" clear value.
    - `qualification_condition` string: Derived read-only condition string rendered from qualification. Strict subset only; tier qualification evaluates post-action counters, not the rule-condition event-start snapshot.
    - `rank` integer: Position in the progression order (higher = better)
    - `updated_at` string, date-time: When this level was last modified (RFC 3339)
  - `lifecycle` TierLifecycle: Time-based tier automation: retention, qualification period, downgrade policy, and counter rollover.
    - `counters` TierCounters: The qualifying counters, and what a `tier_evaluation` does to them.
      - `qualifying` string[]: Counter keys that count toward qualification.
      - `rollover` 'NONE' | 'EXCESS': What a `tier_evaluation` does to qualifying counters. NONE resets them to 0; EXCESS carries the amount above the qualifying threshold into the next period.
    - `downgrade_policy` TierDowngradePolicy: Target level when a participant no longer qualifies.
      - `grace_days` integer: Days to defer a downgrade; the current level is held until grace end.
      - `min_level` string: Level key the participant is never downgraded below.
      - `mode` 'DROP_TO_QUALIFYING' | 'DROP_ONE' | 'HOLD', required: Downgrade mode. DROP_TO_QUALIFYING drops to the highest still-qualifying level; DROP_ONE drops one rank; HOLD keeps the current level.
    - `qualification_period` TierQualificationPeriod: The period that `status_validity` counts from when a `tier_evaluation` runs.
      - `start_day` integer: Day the period starts (1-31). Defaults to 1.
      - `start_month` integer: Month the period starts (1-12). Required when type is FIXED_YEAR.
      - `type` 'CALENDAR_YEAR' | 'FIXED_YEAR' | 'NONE', required: Period type.
    - `retention` TierRetention: How a participant keeps or loses tier status over time.
      - `duration` string: Duration string with w, d, h, m or s units. Required when mode is ACTIVITY_REFRESH; ignored for PERIOD_BASED. Examples: "90d", "1w", or "2160h".
      - `mode` 'PERIOD_BASED' | 'ACTIVITY_REFRESH', required: Retention mode. PERIOD_BASED keeps a level until a `tier_evaluation` re-evaluates it at the end of the qualification period. Scrip schedules that evaluation for a program's tier whose `qualification_period.type` is CALENDAR_YEAR or FIXED_YEAR. It runs at 00:00 UTC on the start date of each new period, for each ACTIVE participant enrolled in the program. Do not add your own cron for it. A NONE period has no end, so Scrip does not schedule one. ACTIVITY_REFRESH extends expiry on qualifying activity.
    - `status_validity` TierStatusValidity: How long a status grant remains valid past the period end.
      - `extend_months` integer: Calendar months added to the end of the period that contains a `tier_evaluation`. The result becomes the `expires_at` of the level kept or assigned. Send 0 or leave it out for no end date.
  - `program_id` string, uuid: Program this tier belongs to
  - `status` 'ACTIVE' | 'ARCHIVED': The tier's lifecycle state.
  - `updated_at` string, date-time: When this tier was last modified (RFC 3339)

## Other responses

- `400`: Validation failed (code: validation_error | invalid_json)
- `401`: The API key is missing or not valid. See [Authentication](https://docs.scrip.dev/authentication).
- `403`: The caller is not allowed to do this.
- `404`: Program or tier not found (code: program_not_found | tier_not_found)
- `409`: Duplicate level key or rank, program is archived, tier is archived, rules still reference the tier you archive, or the tier status changed during the request (code: tier_key_already_exists | program_archived | tier_archived | tier_referenced_by_rules | tier_status_changed)
- `429`: Too many requests. Wait the number of seconds in the `Retry-After` header, then retry. See [Limits and durations](https://docs.scrip.dev/limits#rate-limits).
- `500`: Something failed on Scrip's side. Retry the request, and quote `request_id` if it keeps failing. [Errors](https://docs.scrip.dev/errors) lists every code, and the [API introduction](https://docs.scrip.dev/api-reference/introduction) describes the error format.

---

[API](https://skmtc.dev/scrip/apis/scrip-api.md) · [All operations](https://skmtc.dev/scrip/apis/scrip-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scrip/apis/scrip-api/revisions/c52d35da8d97?raw)
