> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.growthbook.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Saved Group Revisions API

> Edit, review, and publish saved groups through the draft revision REST API.

export const CommercialFeature = ({feature, description}) => {
  const commercialFeatures = {
    "adv-presentations": {
      plan: "enterprise",
      displayName: "Adv Presentations"
    },
    "advanced-permissions": {
      plan: "pro",
      displayName: "Advanced Permissions"
    },
    "ai-byok": {
      plan: "enterprise",
      displayName: "Ai Byok"
    },
    "ai-suggestions": {
      plan: "enterprise",
      displayName: "AI Suggestions"
    },
    archetypes: {
      plan: "pro",
      displayName: "Archetypes"
    },
    "audit-logging": {
      plan: "enterprise",
      displayName: "Audit Logging"
    },
    "cloud-proxy": {
      plan: "pro",
      displayName: "Cloud Proxy"
    },
    "code-references": {
      plan: "pro",
      displayName: "Code References"
    },
    "contextual-bandits": {
      plan: "enterprise",
      displayName: "Contextual Bandits"
    },
    "custom-hooks": {
      plan: "enterprise",
      displayName: "Custom Hooks"
    },
    "custom-launch-checklist": {
      plan: "enterprise",
      displayName: "Custom Launch Checklist"
    },
    "custom-markdown": {
      plan: "enterprise",
      displayName: "Custom Markdown"
    },
    "custom-metadata": {
      plan: "enterprise",
      displayName: "Custom Metadata"
    },
    "custom-roles": {
      plan: "enterprise",
      displayName: "Custom Roles"
    },
    dashboards: {
      plan: "enterprise",
      displayName: "Dashboards"
    },
    "decision-framework": {
      plan: "pro",
      displayName: "Decision Framework"
    },
    "encrypt-features-endpoint": {
      plan: "pro",
      displayName: "Encrypt Features Endpoint"
    },
    "environment-inheritance": {
      plan: "enterprise",
      displayName: "Environment Inheritance"
    },
    "events-forwarder": {
      plan: "pro",
      displayName: "Events Forwarder"
    },
    "experiment-impact": {
      plan: "enterprise",
      displayName: "Experiment Impact"
    },
    "feature-configs": {
      plan: "enterprise",
      displayName: "Feature Configs"
    },
    "funnel-metrics": {
      plan: "pro",
      displayName: "Funnel Metrics"
    },
    "hash-secure-attributes": {
      plan: "pro",
      displayName: "Hash Secure Attributes"
    },
    "historical-power": {
      plan: "pro",
      displayName: "Historical Power"
    },
    holdouts: {
      plan: "enterprise",
      displayName: "Holdouts"
    },
    "incremental-refresh": {
      plan: "enterprise",
      displayName: "Incremental Refresh"
    },
    "json-validation": {
      plan: "enterprise",
      displayName: "JSON Validation"
    },
    "large-saved-groups": {
      plan: "enterprise",
      displayName: "Large Saved Groups"
    },
    learnings: {
      plan: "enterprise",
      displayName: "Learnings"
    },
    livechat: {
      plan: "pro",
      displayName: "Livechat"
    },
    "manage-official-resources": {
      plan: "enterprise",
      displayName: "Manage Official Resources"
    },
    "metric-correlations": {
      plan: "enterprise",
      displayName: "Metric Correlations"
    },
    "metric-effects": {
      plan: "enterprise",
      displayName: "Metric Effects"
    },
    "metric-groups": {
      plan: "enterprise",
      displayName: "Metric Groups"
    },
    "metric-populations": {
      plan: "pro",
      displayName: "Metric Populations"
    },
    "metric-slices": {
      plan: "enterprise",
      displayName: "Metric Slices"
    },
    "multi-armed-bandits": {
      plan: "pro",
      displayName: "Multi Armed Bandits"
    },
    "multi-metric-queries": {
      plan: "enterprise",
      displayName: "Multi Metric Queries"
    },
    "multi-org": {
      plan: "enterprise",
      displayName: "Multi Org"
    },
    "multiple-sdk-webhooks": {
      plan: "pro",
      displayName: "Multiple Sdk Webhooks"
    },
    "no-access-role": {
      plan: "enterprise",
      displayName: "No Access Role"
    },
    "override-metrics": {
      plan: "pro",
      displayName: "Override Metrics"
    },
    "pipeline-mode": {
      plan: "enterprise",
      displayName: "Pipeline Mode"
    },
    "post-stratification": {
      plan: "enterprise",
      displayName: "Post Stratification"
    },
    "precomputed-dimensions": {
      plan: "pro",
      displayName: "Precomputed Dimensions"
    },
    "prerequisite-targeting": {
      plan: "enterprise",
      displayName: "Prerequisite Targeting"
    },
    prerequisites: {
      plan: "pro",
      displayName: "Prerequisites"
    },
    "product-analytics-dashboards": {
      plan: "pro",
      displayName: "Product Analytics Dashboards"
    },
    "project-admin-role": {
      plan: "enterprise",
      displayName: "Project Admin Role"
    },
    "quantile-metrics": {
      plan: "pro",
      displayName: "Quantile Metrics"
    },
    "ramp-schedules": {
      plan: "pro",
      displayName: "Ramp Schedules"
    },
    redirects: {
      plan: "pro",
      displayName: "Redirects"
    },
    "regression-adjustment": {
      plan: "pro",
      displayName: "CUPED"
    },
    releases: {
      plan: "enterprise",
      displayName: "Releases"
    },
    "remote-evaluation": {
      plan: "pro",
      displayName: "Remote Evaluation"
    },
    "require-approvals": {
      plan: "enterprise",
      displayName: "Require Approvals"
    },
    "require-project-for-features-setting": {
      plan: "enterprise",
      displayName: "Require Project For Features Setting"
    },
    "require-project-for-sdk-connections-setting": {
      plan: "enterprise",
      displayName: "Require Project For Sdk Connections Setting"
    },
    "retention-metrics": {
      plan: "pro",
      displayName: "Retention Metrics"
    },
    "safe-rollout": {
      plan: "pro",
      displayName: "Safe Rollout"
    },
    saveSqlExplorerQueries: {
      plan: "pro",
      displayName: "Save SQL Explorer Queries"
    },
    "schedule-feature-flag": {
      plan: "pro",
      displayName: "Schedule Feature Flag"
    },
    "scheduled-revisions": {
      plan: "enterprise",
      displayName: "Scheduled Revisions"
    },
    scim: {
      plan: "enterprise",
      displayName: "SCIM"
    },
    "sequential-testing": {
      plan: "pro",
      displayName: "Sequential Testing"
    },
    "share-product-analytics-dashboards": {
      plan: "enterprise",
      displayName: "Share Product Analytics Dashboards"
    },
    simulate: {
      plan: "pro",
      displayName: "Simulate"
    },
    sso: {
      plan: "enterprise",
      displayName: "SSO"
    },
    "sticky-bucketing": {
      plan: "pro",
      displayName: "Sticky Bucketing"
    },
    teams: {
      plan: "enterprise",
      displayName: "Teams"
    },
    templates: {
      plan: "enterprise",
      displayName: "Templates"
    },
    "unlimited-managed-warehouse-usage": {
      plan: "pro",
      displayName: "Unlimited Managed Warehouse Usage"
    },
    "visual-editor": {
      plan: "pro",
      displayName: "Visual Editor"
    }
  };
  const {plan, displayName} = commercialFeatures[feature];
  const isEnterprise = plan === "enterprise";
  const defaultDescription = isEnterprise ? "is available on Enterprise plans." : "is available on Pro and Enterprise plans.";
  const planLabel = isEnterprise ? "Enterprise" : "Pro";
  const containerStyle = isEnterprise ? {
    backgroundColor: "color-mix(in srgb, var(--indigo-a3) 60%, transparent)"
  } : {
    backgroundColor: "color-mix(in srgb, var(--amber-a3) 60%, transparent)"
  };
  const badgeStyle = isEnterprise ? {
    boxShadow: "inset 0 0 0 1px var(--indigo-a8)",
    color: "var(--indigo-a11)"
  } : {
    boxShadow: "inset 0 0 0 1px var(--amber-a8)",
    color: "var(--amber-a11)"
  };
  return <div className="flex items-start gap-2 mb-4 p-3 text-sm leading-[1.4] rounded-lg" style={containerStyle} role="note">
      <span className="inline-flex items-center justify-center px-1.5 h-5 text-xs font-medium rounded-full shrink-0 leading-none" style={badgeStyle}>
        {planLabel}
      </span>
      <div className="flex-1 leading-[1.3]">
        <strong className="font-semibold">{displayName}</strong>{" "}
        {defaultDescription} {description}
      </div>
    </div>;
};

The saved group revisions REST API is the programmatic counterpart to the UI workflow described in [Publishing & Approval Flows](/features/publishing-and-approval-flows#saved-groups). Every change goes through the same draft → review → publish lifecycle, so a draft you start via REST can be reviewed in the UI (and vice versa).

If you just need to read or replace a saved group atomically — and your org does not require approvals — keep using [`POST /saved-groups/{id}`](/api/#operation/updateSavedGroup) with the `bypassApproval: true` flag. The endpoints below are for callers who need to stage changes, run approvals, or coordinate with the UI revision flow.

The full endpoint reference lives in the [REST API docs](/api/#tag/saved-group-revisions). This page covers the lifecycle and the request shapes you'll typically reach for.

## Drafts and Publishing

When you edit a saved group via the revision endpoints, GrowthBook creates a draft revision. Drafts hold proposed changes against a snapshot of the saved group at the moment the draft was opened — they don't affect SDK evaluations until they're published.

Open a new draft explicitly:

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"title": "Add Q2 beta cohort"}'
```

The response contains the new revision and its integer `version`, which you pass to subsequent edit calls:

```json theme={null}
{
  "revision": {
    "id": "rev_...",
    "version": 4,
    "status": "draft",
    "baseSavedGroup":     { "...": "snapshot taken when the draft opened" },
    "proposedSavedGroup": { "...": "snapshot + proposed changes applied" },
    "proposedChanges": []
  }
}
```

Stage a change on the draft. For list saved groups, prefer the incremental endpoints — they're idempotent and stack on top of the current draft state, so multiple add/remove calls accumulate:

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/items/add' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"items": ["user_42", "user_77"]}'
```

For an atomic full replacement, use `PUT .../values`. For condition saved groups, use `PUT .../condition`. Metadata edits (name, owner, description, projects) go through `PUT .../metadata`. Archive or unarchive with `PUT .../archive`.

Publish to apply the draft to the live saved group:

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/publish' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

After publish, the revision status becomes `merged` and the proposed changes are applied to the live saved group.

### Auto-creating a draft on edit

Every field-edit endpoint (`PUT .../metadata`, `PUT .../condition`, `PUT .../values`, `PUT .../archive`, `POST .../items/add`, `POST .../items/remove`) accepts the literal `"new"` in place of a version number. This opens a fresh draft, applies the edit, and returns the revision in one round trip. Pass `revisionTitle` / `revisionComment` to label the auto-created draft:

```bash theme={null}
curl -X PUT 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/new/metadata' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "revisionTitle": "Rename to internal-beta",
    "name": "internal-beta",
    "description": "Cohort moved to the internal-only beta program"
  }'
```

`revisionTitle` and `revisionComment` are ignored when editing an existing draft.

## Revisions

A saved group has one live revision and zero or more open draft revisions. Each revision has a status:

| Status              | Meaning                                                                   |
| ------------------- | ------------------------------------------------------------------------- |
| `draft`             | Open, editable. Authors can keep staging changes.                         |
| `pending-review`    | Submitted for review. Reviewers can approve, comment, or request changes. |
| `changes-requested` | A reviewer asked for changes. Edit the draft and re-request review.       |
| `approved`          | Approved and ready to publish.                                            |
| `merged`            | Published. Terminal.                                                      |
| `discarded`         | Abandoned. Terminal.                                                      |

Use these endpoints to read revisions:

| Endpoint                                                            | Use it for                                                                                          |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `GET /saved-groups-revisions`                                       | List revisions across every saved group. Filter by `savedGroupId`, `status`, `author`, or `mine`.   |
| `GET /saved-groups-revisions/{savedGroupId}`                        | List revisions for one saved group.                                                                 |
| `GET /saved-groups-revisions/{savedGroupId}/latest`                 | The most recently updated open draft. 404 if no open draft. `mine=true` filters to your own drafts. |
| `GET /saved-groups-revisions/{savedGroupId}/{version}`              | Fetch a specific revision, including `baseSavedGroup`, `proposedSavedGroup`, reviews, activity log. |
| `GET /saved-groups-revisions/{savedGroupId}/{version}/merge-status` | Dry-run merge against the current live state. Returns conflicts and whether they can auto-merge.    |

`baseSavedGroup` is the snapshot taken when the draft opened. `proposedSavedGroup` is what the live saved group would look like if the draft were merged right now — useful for previewing changes without interpreting the raw JSON Patch ops in `proposedChanges`.

### Reverting

`POST /saved-groups-revisions/{savedGroupId}/{version}/revert` creates a new revision whose content matches the specified historical revision. Pass `{"strategy": "draft"}` (default) to stage the revert as a draft, or `{"strategy": "publish"}` to publish immediately. Publish obeys the same approval rules as a normal publish.

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/2/revert' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"strategy": "draft", "title": "Roll back the Q2 cohort change"}'
```

### Discarding

Open drafts you no longer want can be discarded with `POST .../discard`. Merged and already-discarded revisions are rejected.

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/discard' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"reason": "superseded by revision 5"}'
```

## Merge Conflicts

Your draft can diverge from the live saved group if someone else publishes changes while your draft is open. Publishing detects this and rejects the call with `409 Conflict`, including the conflicting fields in the response body.

The recovery flow is to rebase, then re-publish:

```bash theme={null}
# 1. Inspect conflicts
curl 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/merge-status' \
  -H 'Authorization: Bearer YOUR_API_KEY'

# 2. Rebase, picking a resolution strategy per conflicting field
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/rebase' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "conflictResolutions": {
      "values": "union",
      "description": "overwrite"
    }
  }'

# 3. Retry publish
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/publish' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

Per-field resolution strategies:

* `overwrite` — keep the draft's value.
* `discard` — keep the live value.
* `union` — concatenate arrays (only valid for `values` on list saved groups). Pass `customValues` to supply your own merged array instead of the default union.

`merge-status` is purely informational — it doesn't lock anything. If you need strict optimistic locking, call `merge-status`, then `publish`, and on `409` re-fetch and retry.

## Approval Flows

<CommercialFeature feature="require-approvals" />

When [Saved Group approvals are
required](/features/publishing-and-approval-flows#saved-group-approval-flows), a
draft must be approved before it can be published. Approval is not required when
the caller has the **Bypass draft approvals** policy in every Project assigned to
the Saved Group, or when the organization enables **REST API always bypasses
approval requirements**.

### Requesting a Review

Move a draft from `draft` to `pending-review`. Reviewers are notified per the org's approval-flow settings.

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/request-review' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

### Reviewing

A reviewer submits a decision with `POST .../submit-review`. `decision` is one of `approve`, `request-changes`, or `comment`. With `blockSelfApproval` enabled (controlled by the org's **Require approval from a non-editor** setting), authors and contributors cannot submit `approve` on their own drafts.

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/submit-review' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"decision": "approve", "comment": "LGTM"}'
```

The revision's `reviews` array on subsequent reads shows every decision, who made it, and when.

### Publishing with Approvals

Once the revision is `approved`, publish proceeds normally:

```bash theme={null}
curl -X POST 'https://api.growthbook.io/api/v1/saved-groups-revisions/grp_abc123/4/publish' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

There are three ways to publish without an approved revision:

1. **Direct-write bypass** — `POST /saved-groups` and `POST
   /saved-groups/{id}` can send `bypassApproval: true` to update the live Saved
   Group without creating a revision. The caller needs **Bypass draft approvals**
   in every affected Project.
2. **Permission-based publish** — the revision publish endpoint automatically
   skips the approval check for callers with **Bypass draft approvals** in every
   Project assigned to the Saved Group. The `bypassApproval` field on this
   endpoint is accepted for backwards compatibility but does not change the
   result.
3. **Organization-wide REST bypass** — when **REST API always bypasses approval
   requirements** is enabled, REST API requests can publish without approval.

### Permissions

Saved Group lifecycle permissions are independent:

* **Create** creates a Saved Group.
* **Edit** opens and changes drafts, requests review, rebases, and discards
  drafts.
* **Review** approves a draft or requests changes. A plain review comment can
  also be added with the general **Comments** permission.
* **Publish** applies a draft to the live Saved Group and unarchives it.
* **Revert** restores a previously published revision.
* **Archive & delete** archives a Saved Group or permanently deletes one that is
  already archived.
* **Bypass draft approvals** skips required review and allows an out-of-date
  draft to be force-published.

GrowthBook checks permissions again when a draft is published. If the draft
changes the Saved Group's Projects, the caller must have the required permission
in both the current and destination Projects.

A user with only **Revert** or **Archive & delete** can still create and complete
a draft that contains only that action. Adding any unrelated change requires
**Edit** and **Publish** access.
