*.revision.*) carry the revision’s ID, version and base version, status, title, description, dates, author, schedule and the entity it belongs to, plus the event’s own fields such as the reviewer. They also carry its content: for Configs, Constants and Saved Groups, the entity before and after the draft (baseConfig and proposedConfig, for example) and the patch it applies (proposedChanges); for Feature Flags, the draft’s default value, rules, prerequisites and other settings. Reviews and history are on the REST API. Feature Flag revision events also keep the deprecated featureId, date, createdBy and publishedBy fields for existing integrations.
| Event name | Description |
|---|---|
| feature.created | Triggered when a feature is created |
| feature.updated | Triggered when a feature is updated |
| feature.deleted | Triggered when a feature is deleted |
| feature.saferollout.ship | Triggered when a safe rollout is completed and safe to roll out to 100%. |
| feature.saferollout.rollback | Triggered when a safe rollout has a failing guardrail and should be reverted. |
| feature.saferollout.unhealthy | Triggered when a safe rollout is failing a health check and may not be working as expected. |
| feature.rampSchedule.created | Triggered when a ramp schedule is created for a feature |
| feature.rampSchedule.deleted | Triggered when a ramp schedule is deleted from a feature |
| feature.rampSchedule.actions.started | Triggered when a feature ramp schedule starts |
| feature.rampSchedule.actions.completed | Triggered when a feature ramp schedule completes all steps |
| feature.rampSchedule.actions.rolledBack | Triggered when a feature ramp schedule is rolled back or reset to start |
| feature.rampSchedule.actions.jumped | Triggered when a feature ramp schedule is jumped to a specific step |
| feature.rampSchedule.actions.step.advanced | Triggered when a feature ramp schedule advances. Overdue steps are caught up in a single advance: when currentStepIndex - previousStepIndex > 1, the intermediate steps were folded into this one event (one revision publish) rather than fired individually. |
| feature.rampSchedule.actions.step.approvalRequired | Triggered when a feature ramp step is waiting for approval |
| feature.rampSchedule.actions.awaitingStartApproval | Triggered when a feature ramp schedule is published but held at the start, awaiting an explicit start approval |
| feature.rampSchedule.actions.startApproved | Triggered when a held ramp schedule’s start is approved by a user |
| feature.rampSchedule.actions.errorPaused | Triggered when a feature ramp schedule pauses because a step could not be applied, for example a plan the engine refuses; reason says why |
| feature.rampSchedule.actions.stepHeld | Triggered when a monitored ramp step is held by a health check (SRM, multiple exposures, no traffic, a guardrail metric that failed to compute, an unhealthy signal metric). Sent once per check per step; reason says why |
| feature.rampSchedule.actions.paused | Triggered when a feature ramp schedule is paused by a user or by its monitoring policy |
| feature.rampSchedule.actions.resumed | Triggered when a paused feature ramp schedule is resumed |
| feature.revision.created | Triggered when a new draft revision is created for a feature |
| feature.revision.updated | Triggered when a draft revision is modified (rules, default value, toggles, prerequisites, metadata, etc.). The change field indicates the specific kind of mutation. |
| feature.revision.reviewRequested | Triggered when a draft revision is submitted for review |
| feature.revision.approved | Triggered when a draft revision is approved by a reviewer |
| feature.revision.changesRequested | Triggered when a reviewer requests changes on a draft revision |
| feature.revision.commented | Triggered when a comment is added to a draft revision |
| feature.revision.discarded | Triggered when a draft revision is discarded |
| feature.revision.reopened | Triggered when a discarded draft revision is reopened as a draft |
| feature.revision.recalled | Triggered when the author (or an editor) recalls a review request, returning the revision to draft. Distinct from revision.reopened, which restores a discarded revision. |
| feature.revision.reviewRetracted | Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end up pending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched. |
| feature.revision.publishScheduleChanged | Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change. |
| feature.revision.rebased | Triggered when a draft revision is rebased onto the latest published version |
| feature.revision.published | Triggered when a draft revision is published. Overlaps with feature.updated but identifies the revision and its author. |
| feature.revision.reverted | Triggered when a feature is reverted to a previous published revision |
| feature.revision.publishFailed | Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve. |
| experiment.created | Triggered when an experiment is created |
| experiment.updated | Triggered when an experiment is updated |
| experiment.deleted | Triggered when an experiment is deleted |
| experiment.warning | Triggered when a warning condition is detected on an experiment, such as a sample ratio mismatch, multiple exposures, low power, no data, or a failed results update. The type field identifies the condition. |
| experiment.status.started | Triggered when an experiment starts or resumes running. |
| experiment.status.stopped | Triggered when an experiment stops, including its result and any temporary rollout. |
| experiment.status.endingSoon | Triggered when a running experiment is within 3 days of its scheduled end date. |
| experiment.status.stale | Triggered when a running experiment has been active for a long time without a decision. |
| experiment.guardrailFailed | Triggered when a running experiment has a failing guardrail metric. |
| experiment.bandit.weightsChanged | Triggered when a multi-armed bandit materially changes variation weights. |
| experiment.info.significance | Triggered when a goal or guardrail metric reaches significance in an experiment (e.g. either above 95% or below 5% chance to win). Be careful using this without Sequential Testing as it can lead to peeking problems. |
| experiment.info.scheduled-status-update | Triggered when a scheduled start or stop is automatically applied to an experiment, including the auto-ship outcome for a scheduled end. |
| experiment.decision.ship | Triggered when an experiment is ready to ship a variation. |
| experiment.decision.rollback | Triggered when an experiment should be rolled back to the control. |
| experiment.decision.review | Triggered when an experiment has reached the desired power point, but the results may be ambiguous. |
| holdout.created | Triggered when a holdout is created. |
| holdout.status.changed | Triggered when a holdout moves between its lifecycle stages, such as running or analysis. |
| holdout.config.newLinkage | Triggered when feature flags or experiments are linked to a holdout. |
| sdkConnection.payloadSize.warning | Triggered when an SDK Connection’s payload grows past half, 80%, or all of the 16 MB its cache can store. The level field is warning, danger, or over-limit, and recommendations lists ways to shrink it. Fires once per level reached, not on every publish. |
| savedGroup.created | Triggered when a saved group is created |
| savedGroup.updated | Triggered when a saved group is updated |
| savedGroup.deleted | Triggered when a saved group is deleted |
| savedGroup.revision.created | Triggered when a new draft revision is created for a saved group |
| savedGroup.revision.updated | Triggered when a draft revision’s proposed changes are modified (values, condition, archive, or metadata). The change field indicates the kind of mutation. |
| savedGroup.revision.reviewRequested | Triggered when a draft revision is submitted for review |
| savedGroup.revision.approved | Triggered when a draft revision is approved by a reviewer |
| savedGroup.revision.changesRequested | Triggered when a reviewer requests changes on a draft revision |
| savedGroup.revision.commented | Triggered when a comment is added to a draft revision |
| savedGroup.revision.discarded | Triggered when a draft revision is discarded |
| savedGroup.revision.rebased | Triggered when a draft revision is rebased onto the latest live state |
| savedGroup.revision.published | Triggered when a draft revision is published. Overlaps with savedGroup.updated but provides revision-specific context. |
| savedGroup.revision.reverted | Triggered when a saved group is reverted to a previous published revision |
| savedGroup.revision.reopened | Triggered when a discarded revision is reopened |
| savedGroup.revision.recalled | Triggered when the author (or an editor) recalls a review request, returning the revision to draft. Distinct from revision.reopened, which restores a discarded revision. |
| savedGroup.revision.reviewRetracted | Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end up pending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched. |
| savedGroup.revision.publishScheduleChanged | Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change. |
| savedGroup.revision.publishFailed | Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve. |
| constant.created | Triggered when a constant is created |
| constant.updated | Triggered when a constant is updated |
| constant.deleted | Triggered when a constant is deleted |
| constant.revision.created | Triggered when a new draft revision is created for a constant |
| constant.revision.updated | Triggered when a draft revision’s proposed changes are modified (value, archive, or metadata). The change field indicates the kind of mutation. |
| constant.revision.reviewRequested | Triggered when a draft revision is submitted for review |
| constant.revision.approved | Triggered when a draft revision is approved by a reviewer |
| constant.revision.changesRequested | Triggered when a reviewer requests changes on a draft revision |
| constant.revision.commented | Triggered when a comment is added to a draft revision |
| constant.revision.discarded | Triggered when a draft revision is discarded |
| constant.revision.rebased | Triggered when a draft revision is rebased onto the latest live state |
| constant.revision.published | Triggered when a draft revision is published. Overlaps with constant.updated but provides revision-specific context. |
| constant.revision.reverted | Triggered when a constant is reverted to a previous published revision |
| constant.revision.reopened | Triggered when a discarded revision is reopened |
| constant.revision.recalled | Triggered when the author (or an editor) recalls a review request, returning the revision to draft. Distinct from revision.reopened, which restores a discarded revision. |
| constant.revision.reviewRetracted | Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end up pending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched. |
| constant.revision.publishScheduleChanged | Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change. |
| constant.revision.publishFailed | Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve. |
| config.created | Triggered when a config is created |
| config.updated | Triggered when a config is updated |
| config.deleted | Triggered when a config is deleted |
| config.revision.created | Triggered when a new draft revision is created for a config |
| config.revision.updated | Triggered when a draft revision’s proposed changes are modified (value, schema, archive, or metadata). The change field indicates the kind of mutation. |
| config.revision.reviewRequested | Triggered when a draft revision is submitted for review |
| config.revision.approved | Triggered when a draft revision is approved by a reviewer |
| config.revision.changesRequested | Triggered when a reviewer requests changes on a draft revision |
| config.revision.commented | Triggered when a comment is added to a draft revision |
| config.revision.discarded | Triggered when a draft revision is discarded |
| config.revision.rebased | Triggered when a draft revision is rebased onto the latest live state |
| config.revision.published | Triggered when a draft revision is published. Overlaps with config.updated but provides revision-specific context. |
| config.revision.reverted | Triggered when a config is reverted to a previous published revision |
| config.revision.reopened | Triggered when a discarded revision is reopened |
| config.revision.recalled | Triggered when the author (or an editor) recalls a review request, returning the revision to draft. Distinct from revision.reopened, which restores a discarded revision. |
| config.revision.reviewRetracted | Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end up pending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched. |
| config.revision.publishScheduleChanged | Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change. |
| config.revision.publishFailed | Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve. |
| user.login | Triggered when a user logs in |
| apiKey.expiring | Triggered once when a secret API key is within a week of expiring |
| apiKey.expired | Triggered once when a secret API key passes its expiration date |
feature.created
Triggered when a feature is createdPayload
Payload
{
event: "feature.created";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id: string;
dateCreated: string;
dateUpdated: string;
archived: boolean;
description: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
project: string;
valueType: "boolean" | "string" | "number" | "json";
defaultValue: string;
tags: string[];
environments: Record<string, any>;
/** Feature IDs. Each feature must evaluate to `true` */
prerequisites?: string[] | undefined;
revision: {
version: number;
comment: string;
date: string;
createdBy: string;
publishedBy: string;
};
customFields?: Record<string, {}> | undefined;
holdout?: ({
/** Holdout ID */
id: string;
/** The feature value assigned to users in the holdout treatment group */
value: string;
} | null) | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.updated
Triggered when a feature is updatedPayload
Payload
{
event: "feature.updated";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id: string;
dateCreated: string;
dateUpdated: string;
archived: boolean;
description: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
project: string;
valueType: "boolean" | "string" | "number" | "json";
defaultValue: string;
tags: string[];
environments: Record<string, any>;
/** Feature IDs. Each feature must evaluate to `true` */
prerequisites?: string[] | undefined;
revision: {
version: number;
comment: string;
date: string;
createdBy: string;
publishedBy: string;
};
customFields?: Record<string, {}> | undefined;
holdout?: ({
/** Holdout ID */
id: string;
/** The feature value assigned to users in the holdout treatment group */
value: string;
} | null) | undefined;
};
previous_attributes: {
id?: string | undefined;
dateCreated?: string | undefined;
dateUpdated?: string | undefined;
archived?: boolean | undefined;
description?: string | undefined;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
project?: string | undefined;
valueType?: ("boolean" | "string" | "number" | "json") | undefined;
defaultValue?: string | undefined;
tags?: string[] | undefined;
environments?: Record<string, any> | undefined;
/** Feature IDs. Each feature must evaluate to `true` */
prerequisites?: string[] | undefined;
revision?: {
version: number;
comment: string;
date: string;
createdBy: string;
publishedBy: string;
} | undefined;
customFields?: Record<string, {}> | undefined;
holdout?: ({
/** Holdout ID */
id: string;
/** The feature value assigned to users in the holdout treatment group */
value: string;
} | null) | undefined;
};
changes?: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
modified: unknown[];
} | undefined;
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.deleted
Triggered when a feature is deletedPayload
Payload
{
event: "feature.deleted";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id: string;
dateCreated: string;
dateUpdated: string;
archived: boolean;
description: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
project: string;
valueType: "boolean" | "string" | "number" | "json";
defaultValue: string;
tags: string[];
environments: Record<string, any>;
/** Feature IDs. Each feature must evaluate to `true` */
prerequisites?: string[] | undefined;
revision: {
version: number;
comment: string;
date: string;
createdBy: string;
publishedBy: string;
};
customFields?: Record<string, {}> | undefined;
holdout?: ({
/** Holdout ID */
id: string;
/** The feature value assigned to users in the holdout treatment group */
value: string;
} | null) | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.saferollout.ship
Triggered when a safe rollout is completed and safe to roll out to 100%.Payload
Payload
{
event: "feature.saferollout.ship";
object: "feature";
api_version: string;
created: number;
data: {
object: {
featureId: string;
safeRolloutId: string;
environment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.saferollout.rollback
Triggered when a safe rollout has a failing guardrail and should be reverted.Payload
Payload
{
event: "feature.saferollout.rollback";
object: "feature";
api_version: string;
created: number;
data: {
object: {
featureId: string;
safeRolloutId: string;
environment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.saferollout.unhealthy
Triggered when a safe rollout is failing a health check and may not be working as expected.Payload
Payload
{
event: "feature.saferollout.unhealthy";
object: "feature";
api_version: string;
created: number;
data: {
object: {
featureId: string;
safeRolloutId: string;
environment: string;
unhealthyReason: ("srm" | "multipleExposures")[];
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.created
Triggered when a ramp schedule is created for a featurePayload
Payload
{
event: "feature.rampSchedule.created";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
entityType: string;
entityId: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.deleted
Triggered when a ramp schedule is deleted from a featurePayload
Payload
{
event: "feature.rampSchedule.deleted";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.started
Triggered when a feature ramp schedule startsPayload
Payload
{
event: "feature.rampSchedule.actions.started";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.completed
Triggered when a feature ramp schedule completes all stepsPayload
Payload
{
event: "feature.rampSchedule.actions.completed";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
previousStepIndex?: number | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.rolledBack
Triggered when a feature ramp schedule is rolled back or reset to startPayload
Payload
{
event: "feature.rampSchedule.actions.rolledBack";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
targetStepIndex: number;
reason?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.jumped
Triggered when a feature ramp schedule is jumped to a specific stepPayload
Payload
{
event: "feature.rampSchedule.actions.jumped";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
targetStepIndex: number;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.step.advanced
Triggered when a feature ramp schedule advances. Overdue steps are caught up in a single advance: whencurrentStepIndex - previousStepIndex > 1, the intermediate steps were folded into this one event (one revision publish) rather than fired individually.
Payload
Payload
{
event: "feature.rampSchedule.actions.step.advanced";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
previousStepIndex?: number | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.step.approvalRequired
Triggered when a feature ramp step is waiting for approvalPayload
Payload
{
event: "feature.rampSchedule.actions.step.approvalRequired";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
approvalNotes?: (string | null) | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.awaitingStartApproval
Triggered when a feature ramp schedule is published but held at the start, awaiting an explicit start approvalPayload
Payload
{
event: "feature.rampSchedule.actions.awaitingStartApproval";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.startApproved
Triggered when a held ramp schedule’s start is approved by a userPayload
Payload
{
event: "feature.rampSchedule.actions.startApproved";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.errorPaused
Triggered when a feature ramp schedule pauses because a step could not be applied, for example a plan the engine refuses;reason says why
Payload
Payload
{
event: "feature.rampSchedule.actions.errorPaused";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
reason: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.stepHeld
Triggered when a monitored ramp step is held by a health check (SRM, multiple exposures, no traffic, a guardrail metric that failed to compute, an unhealthy signal metric). Sent once per check per step;reason says why
Payload
Payload
{
event: "feature.rampSchedule.actions.stepHeld";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
reason: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.paused
Triggered when a feature ramp schedule is paused by a user or by its monitoring policyPayload
Payload
{
event: "feature.rampSchedule.actions.paused";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
reason?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.rampSchedule.actions.resumed
Triggered when a paused feature ramp schedule is resumedPayload
Payload
{
event: "feature.rampSchedule.actions.resumed";
object: "feature";
api_version: string;
created: number;
data: {
object: {
rampScheduleId: string;
rampName: string;
orgId: string;
currentStepIndex: number;
status: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.created
Triggered when a new draft revision is created for a featurePayload
Payload
{
event: "feature.revision.created";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.updated
Triggered when a draft revision is modified (rules, default value, toggles, prerequisites, metadata, etc.). Thechange field indicates the specific kind of mutation.
Payload
Payload
{
event: "feature.revision.updated";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
change: "rule.add" | "rule.update" | "rule.delete" | "rule.reorder" | "rule.rampSchedule.set" | "rule.rampSchedule.remove" | "toggle" | "defaultValue" | "prerequisites" | "holdout" | "archive" | "metadata";
environments?: string[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.reviewRequested
Triggered when a draft revision is submitted for reviewPayload
Payload
{
event: "feature.revision.reviewRequested";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.approved
Triggered when a draft revision is approved by a reviewerPayload
Payload
{
event: "feature.revision.approved";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.changesRequested
Triggered when a reviewer requests changes on a draft revisionPayload
Payload
{
event: "feature.revision.changesRequested";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.commented
Triggered when a comment is added to a draft revisionPayload
Payload
{
event: "feature.revision.commented";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.discarded
Triggered when a draft revision is discardedPayload
Payload
{
event: "feature.revision.discarded";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.reopened
Triggered when a discarded draft revision is reopened as a draftPayload
Payload
{
event: "feature.revision.reopened";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.recalled
Triggered when the author (or an editor) recalls a review request, returning the revision todraft. Distinct from revision.reopened, which restores a discarded revision.
Payload
Payload
{
event: "feature.revision.recalled";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.reviewRetracted
Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end uppending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched.
Payload
Payload
{
event: "feature.revision.reviewRetracted";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.publishScheduleChanged
Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change.Payload
Payload
{
event: "feature.revision.publishScheduleChanged";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.rebased
Triggered when a draft revision is rebased onto the latest published versionPayload
Payload
{
event: "feature.revision.rebased";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.published
Triggered when a draft revision is published. Overlaps withfeature.updated but identifies the revision and its author.
Payload
Payload
{
event: "feature.revision.published";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.reverted
Triggered when a feature is reverted to a previous published revisionPayload
Payload
{
event: "feature.revision.reverted";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
revertedToVersion: number;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
feature.revision.publishFailed
Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve.Payload
Payload
{
event: "feature.revision.publishFailed";
object: "feature";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
feature: {
id: string;
};
/** Deprecated: use `feature.id`. */
featureId: string;
/** Deprecated: use `dateCreated`. */
date: string;
createdBy?: string | undefined;
publishedBy?: string | undefined;
/** The draft's default value */
defaultValue?: string | undefined;
rules: Record<string, unknown[]>;
environmentsEnabled?: Record<string, boolean> | undefined;
prerequisites?: {
/** Feature ID */
id: string;
condition: string;
}[] | undefined;
metadata?: {} | undefined;
/** Ramp schedule steps the draft attaches to its rules */
rampActions?: unknown[] | undefined;
bulkPublishId?: string | undefined;
failureReason: string;
terminal: boolean;
attempts: number;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.created
Triggered when an experiment is createdPayload
Payload
{
event: "experiment.created";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
id: string;
trackingKey: string;
dateCreated: string;
dateUpdated: string;
name: string;
type: "standard" | "multi-armed-bandit" | "holdout";
project: string;
hypothesis: string;
description: string;
tags: string[];
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
ownerEmail?: string | undefined;
archived: boolean;
status: string;
autoRefresh: boolean;
hashAttribute: string;
fallbackAttribute?: string | undefined;
hashVersion: 1 | 2;
disableStickyBucketing?: boolean | undefined;
bucketVersion?: number | undefined;
minBucketVersion?: number | undefined;
variations: {
variationId: string;
key: string;
name: string;
description: string;
screenshots: string[];
}[];
phases: {
name: string;
dateStarted: string;
dateEnded: string;
reasonForStopping: string;
seed: string;
coverage: number;
trafficSplit: {
variationId: string;
weight: number;
}[];
namespace?: {
namespaceId: string;
enabled?: boolean | undefined;
range?: number[] | undefined;
ranges?: [
number,
number
][] | undefined;
} | undefined;
targetingCondition: string;
prerequisites?: {
id: string;
condition: string;
}[] | undefined;
savedGroupTargeting?: {
matchType: "all" | "any" | "none";
savedGroups: string[];
}[] | undefined;
}[];
settings: {
datasourceId: string;
/** The assignment query, grouping its ID with the identifier type analyzed on. */
assignmentQuery: {
/** The ID of one of the data source's assignment queries. */
id: string;
/** The identifier type analyzed on. Null when none can be resolved: no assignment query is selected, or a record saved before identifier types were stored points at a query that no longer exists. */
identifierType: string | null;
};
/** Deprecated: use `assignmentQuery`. */
assignmentQueryId: string;
experimentId: string;
segmentId: string;
queryFilter: string;
inProgressConversions: "include" | "exclude";
/** Setting attribution model to `"experimentDuration"` is the same as selecting "Ignore Conversion Windows" for the Conversion Window Override. Setting it to `"lookbackOverride"` requires a `lookbackOverride` object to be provided. */
attributionModel: "firstExposure" | "experimentDuration" | "lookbackOverride";
/** Controls the lookback override for the experiment. For type "window", value must be a non-negative number and valueUnit is required. */
lookbackOverride?: {
type: "date" | "window";
/** For "window" type - non-negative numeric value (e.g. 7 for 7 days). For "date" type a date string. */
value: number | string;
/** Used when type is "window". Defaults to "days". */
valueUnit?: ("minutes" | "hours" | "days" | "weeks") | undefined;
} | undefined;
statsEngine: "bayesian" | "frequentist";
regressionAdjustmentEnabled?: boolean | undefined;
sequentialTestingEnabled?: boolean | undefined;
sequentialTestingTuningParameter?: number | undefined;
/** When null, the organization default is used. */
postStratificationEnabled?: (boolean | null) | undefined;
/** Controls the decision framework and metric overrides for the experiment. Replaces the entire stored object on update (does not patch individual fields). */
decisionFrameworkSettings?: {
decisionCriteriaId?: string | undefined;
decisionFrameworkMetricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
/** The target relative MDE to use for the metric, expressed as proportions (e.g. use 0.1 for 10%). Must be greater than 0. */
targetMDE?: number | undefined;
}[] | undefined;
} | undefined;
/** Per-metric analysis overrides; also reflected in goals/secondaryMetrics/guardrails overrides when applicable. On create/update, this replaces the entire stored array (it does not patch individual entries). */
metricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
windowType?: ("conversion" | "lookback" | "") | undefined;
windowHours?: number | undefined;
delayHours?: number | undefined;
/** Must be true for the override to take effect. If true, the other proper prior settings in this object will be used if present. */
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
/** Must be true for the override to take effect. If true, the other regression adjustment settings in this object will be used if present. */
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
}[] | undefined;
goals: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
secondaryMetrics: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
guardrails: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
activationMetric?: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
} | undefined;
};
resultSummary?: {
status: string;
winner: string;
conclusions: string;
releasedVariationId: string;
excludeFromPayload: boolean;
} | undefined;
shareLevel?: ("public" | "organization") | undefined;
publicUrl?: string | undefined;
banditScheduleValue?: number | undefined;
banditScheduleUnit?: ("days" | "hours") | undefined;
banditBurnInValue?: number | undefined;
banditBurnInUnit?: ("days" | "hours") | undefined;
banditConversionWindowValue?: number | undefined;
banditConversionWindowUnit?: ("days" | "hours") | undefined;
linkedFeatures?: string[] | undefined;
attributeScopeAllProjects?: boolean | undefined;
hasVisualChangesets?: boolean | undefined;
hasURLRedirects?: boolean | undefined;
customFields?: Record<string, any> | undefined;
/** Custom slices that apply to ALL applicable metrics in the experiment */
customMetricSlices?: {
slices: {
column: string;
levels: string[];
}[];
}[] | undefined;
precomputedUnitDimensionIds?: string[] | undefined;
/** ID of the default dashboard for this experiment. */
defaultDashboardId?: string | undefined;
templateId?: string | undefined;
statusUpdateSchedule?: ({
startAt?: string | undefined;
stopAt?: string | undefined;
/** Relative end offset. Deferred: resolved to a concrete `stopAt` at the experiment's actual start (or off `dateStarted` when already running). */
stopAfter?: {
value: number;
unit: "hours" | "days";
} | undefined;
/** What happens at the scheduled end date. `notify` keeps the experiment running and just notifies (soft). `auto-ship` (requires the Decision Framework) ships the winning variation and stops; multi-winner ties break on `tiebreakerMetricId` (higher lift); with no clear winner, `fallback` either keeps running (`notify`) or ships `fallbackVariationId`. `force-ship` stops and rolls out `fallbackVariationId`. `stop` is a hard deadline that stops with no rollout. For `force-ship` and `stop`, the Decision Framework verdict (won/lost/inconclusive) is recorded as metadata when available. */
scheduledStopPlan?: {
mode: "notify" | "auto-ship" | "force-ship" | "stop";
tiebreakerMetricId?: string | undefined;
/** What to do at the scheduled end when there is no clear winner. Required when `mode` is `"auto-ship"`; ignored for other modes. */
fallback?: ("notify" | "force-ship") | undefined;
fallbackVariationId?: string | undefined;
} | undefined;
} | null) | undefined;
nextScheduledStatusUpdate?: ({
type: "start" | "stop";
date: string;
} | null) | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.updated
Triggered when an experiment is updatedPayload
Payload
{
event: "experiment.updated";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
id: string;
trackingKey: string;
dateCreated: string;
dateUpdated: string;
name: string;
type: "standard" | "multi-armed-bandit" | "holdout";
project: string;
hypothesis: string;
description: string;
tags: string[];
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
ownerEmail?: string | undefined;
archived: boolean;
status: string;
autoRefresh: boolean;
hashAttribute: string;
fallbackAttribute?: string | undefined;
hashVersion: 1 | 2;
disableStickyBucketing?: boolean | undefined;
bucketVersion?: number | undefined;
minBucketVersion?: number | undefined;
variations: {
variationId: string;
key: string;
name: string;
description: string;
screenshots: string[];
}[];
phases: {
name: string;
dateStarted: string;
dateEnded: string;
reasonForStopping: string;
seed: string;
coverage: number;
trafficSplit: {
variationId: string;
weight: number;
}[];
namespace?: {
namespaceId: string;
enabled?: boolean | undefined;
range?: number[] | undefined;
ranges?: [
number,
number
][] | undefined;
} | undefined;
targetingCondition: string;
prerequisites?: {
id: string;
condition: string;
}[] | undefined;
savedGroupTargeting?: {
matchType: "all" | "any" | "none";
savedGroups: string[];
}[] | undefined;
}[];
settings: {
datasourceId: string;
/** The assignment query, grouping its ID with the identifier type analyzed on. */
assignmentQuery: {
/** The ID of one of the data source's assignment queries. */
id: string;
/** The identifier type analyzed on. Null when none can be resolved: no assignment query is selected, or a record saved before identifier types were stored points at a query that no longer exists. */
identifierType: string | null;
};
/** Deprecated: use `assignmentQuery`. */
assignmentQueryId: string;
experimentId: string;
segmentId: string;
queryFilter: string;
inProgressConversions: "include" | "exclude";
/** Setting attribution model to `"experimentDuration"` is the same as selecting "Ignore Conversion Windows" for the Conversion Window Override. Setting it to `"lookbackOverride"` requires a `lookbackOverride` object to be provided. */
attributionModel: "firstExposure" | "experimentDuration" | "lookbackOverride";
/** Controls the lookback override for the experiment. For type "window", value must be a non-negative number and valueUnit is required. */
lookbackOverride?: {
type: "date" | "window";
/** For "window" type - non-negative numeric value (e.g. 7 for 7 days). For "date" type a date string. */
value: number | string;
/** Used when type is "window". Defaults to "days". */
valueUnit?: ("minutes" | "hours" | "days" | "weeks") | undefined;
} | undefined;
statsEngine: "bayesian" | "frequentist";
regressionAdjustmentEnabled?: boolean | undefined;
sequentialTestingEnabled?: boolean | undefined;
sequentialTestingTuningParameter?: number | undefined;
/** When null, the organization default is used. */
postStratificationEnabled?: (boolean | null) | undefined;
/** Controls the decision framework and metric overrides for the experiment. Replaces the entire stored object on update (does not patch individual fields). */
decisionFrameworkSettings?: {
decisionCriteriaId?: string | undefined;
decisionFrameworkMetricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
/** The target relative MDE to use for the metric, expressed as proportions (e.g. use 0.1 for 10%). Must be greater than 0. */
targetMDE?: number | undefined;
}[] | undefined;
} | undefined;
/** Per-metric analysis overrides; also reflected in goals/secondaryMetrics/guardrails overrides when applicable. On create/update, this replaces the entire stored array (it does not patch individual entries). */
metricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
windowType?: ("conversion" | "lookback" | "") | undefined;
windowHours?: number | undefined;
delayHours?: number | undefined;
/** Must be true for the override to take effect. If true, the other proper prior settings in this object will be used if present. */
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
/** Must be true for the override to take effect. If true, the other regression adjustment settings in this object will be used if present. */
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
}[] | undefined;
goals: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
secondaryMetrics: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
guardrails: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
activationMetric?: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
} | undefined;
};
resultSummary?: {
status: string;
winner: string;
conclusions: string;
releasedVariationId: string;
excludeFromPayload: boolean;
} | undefined;
shareLevel?: ("public" | "organization") | undefined;
publicUrl?: string | undefined;
banditScheduleValue?: number | undefined;
banditScheduleUnit?: ("days" | "hours") | undefined;
banditBurnInValue?: number | undefined;
banditBurnInUnit?: ("days" | "hours") | undefined;
banditConversionWindowValue?: number | undefined;
banditConversionWindowUnit?: ("days" | "hours") | undefined;
linkedFeatures?: string[] | undefined;
attributeScopeAllProjects?: boolean | undefined;
hasVisualChangesets?: boolean | undefined;
hasURLRedirects?: boolean | undefined;
customFields?: Record<string, any> | undefined;
/** Custom slices that apply to ALL applicable metrics in the experiment */
customMetricSlices?: {
slices: {
column: string;
levels: string[];
}[];
}[] | undefined;
precomputedUnitDimensionIds?: string[] | undefined;
/** ID of the default dashboard for this experiment. */
defaultDashboardId?: string | undefined;
templateId?: string | undefined;
statusUpdateSchedule?: ({
startAt?: string | undefined;
stopAt?: string | undefined;
/** Relative end offset. Deferred: resolved to a concrete `stopAt` at the experiment's actual start (or off `dateStarted` when already running). */
stopAfter?: {
value: number;
unit: "hours" | "days";
} | undefined;
/** What happens at the scheduled end date. `notify` keeps the experiment running and just notifies (soft). `auto-ship` (requires the Decision Framework) ships the winning variation and stops; multi-winner ties break on `tiebreakerMetricId` (higher lift); with no clear winner, `fallback` either keeps running (`notify`) or ships `fallbackVariationId`. `force-ship` stops and rolls out `fallbackVariationId`. `stop` is a hard deadline that stops with no rollout. For `force-ship` and `stop`, the Decision Framework verdict (won/lost/inconclusive) is recorded as metadata when available. */
scheduledStopPlan?: {
mode: "notify" | "auto-ship" | "force-ship" | "stop";
tiebreakerMetricId?: string | undefined;
/** What to do at the scheduled end when there is no clear winner. Required when `mode` is `"auto-ship"`; ignored for other modes. */
fallback?: ("notify" | "force-ship") | undefined;
fallbackVariationId?: string | undefined;
} | undefined;
} | null) | undefined;
nextScheduledStatusUpdate?: ({
type: "start" | "stop";
date: string;
} | null) | undefined;
};
previous_attributes: {
id?: string | undefined;
trackingKey?: string | undefined;
dateCreated?: string | undefined;
dateUpdated?: string | undefined;
name?: string | undefined;
type?: ("standard" | "multi-armed-bandit" | "holdout") | undefined;
project?: string | undefined;
hypothesis?: string | undefined;
description?: string | undefined;
tags?: string[] | undefined;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
archived?: boolean | undefined;
status?: string | undefined;
autoRefresh?: boolean | undefined;
hashAttribute?: string | undefined;
fallbackAttribute?: string | undefined;
hashVersion?: (1 | 2) | undefined;
disableStickyBucketing?: boolean | undefined;
bucketVersion?: number | undefined;
minBucketVersion?: number | undefined;
variations?: {
variationId: string;
key: string;
name: string;
description: string;
screenshots: string[];
}[] | undefined;
phases?: {
name: string;
dateStarted: string;
dateEnded: string;
reasonForStopping: string;
seed: string;
coverage: number;
trafficSplit: {
variationId: string;
weight: number;
}[];
namespace?: {
namespaceId: string;
enabled?: boolean | undefined;
range?: number[] | undefined;
ranges?: [
number,
number
][] | undefined;
} | undefined;
targetingCondition: string;
prerequisites?: {
id: string;
condition: string;
}[] | undefined;
savedGroupTargeting?: {
matchType: "all" | "any" | "none";
savedGroups: string[];
}[] | undefined;
}[] | undefined;
settings?: {
datasourceId: string;
/** The assignment query, grouping its ID with the identifier type analyzed on. */
assignmentQuery: {
/** The ID of one of the data source's assignment queries. */
id: string;
/** The identifier type analyzed on. Null when none can be resolved: no assignment query is selected, or a record saved before identifier types were stored points at a query that no longer exists. */
identifierType: string | null;
};
/** Deprecated: use `assignmentQuery`. */
assignmentQueryId: string;
experimentId: string;
segmentId: string;
queryFilter: string;
inProgressConversions: "include" | "exclude";
/** Setting attribution model to `"experimentDuration"` is the same as selecting "Ignore Conversion Windows" for the Conversion Window Override. Setting it to `"lookbackOverride"` requires a `lookbackOverride` object to be provided. */
attributionModel: "firstExposure" | "experimentDuration" | "lookbackOverride";
/** Controls the lookback override for the experiment. For type "window", value must be a non-negative number and valueUnit is required. */
lookbackOverride?: {
type: "date" | "window";
/** For "window" type - non-negative numeric value (e.g. 7 for 7 days). For "date" type a date string. */
value: number | string;
/** Used when type is "window". Defaults to "days". */
valueUnit?: ("minutes" | "hours" | "days" | "weeks") | undefined;
} | undefined;
statsEngine: "bayesian" | "frequentist";
regressionAdjustmentEnabled?: boolean | undefined;
sequentialTestingEnabled?: boolean | undefined;
sequentialTestingTuningParameter?: number | undefined;
/** When null, the organization default is used. */
postStratificationEnabled?: (boolean | null) | undefined;
/** Controls the decision framework and metric overrides for the experiment. Replaces the entire stored object on update (does not patch individual fields). */
decisionFrameworkSettings?: {
decisionCriteriaId?: string | undefined;
decisionFrameworkMetricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
/** The target relative MDE to use for the metric, expressed as proportions (e.g. use 0.1 for 10%). Must be greater than 0. */
targetMDE?: number | undefined;
}[] | undefined;
} | undefined;
/** Per-metric analysis overrides; also reflected in goals/secondaryMetrics/guardrails overrides when applicable. On create/update, this replaces the entire stored array (it does not patch individual entries). */
metricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
windowType?: ("conversion" | "lookback" | "") | undefined;
windowHours?: number | undefined;
delayHours?: number | undefined;
/** Must be true for the override to take effect. If true, the other proper prior settings in this object will be used if present. */
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
/** Must be true for the override to take effect. If true, the other regression adjustment settings in this object will be used if present. */
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
}[] | undefined;
goals: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
secondaryMetrics: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
guardrails: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
activationMetric?: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
} | undefined;
} | undefined;
resultSummary?: {
status: string;
winner: string;
conclusions: string;
releasedVariationId: string;
excludeFromPayload: boolean;
} | undefined;
shareLevel?: ("public" | "organization") | undefined;
publicUrl?: string | undefined;
banditScheduleValue?: number | undefined;
banditScheduleUnit?: ("days" | "hours") | undefined;
banditBurnInValue?: number | undefined;
banditBurnInUnit?: ("days" | "hours") | undefined;
banditConversionWindowValue?: number | undefined;
banditConversionWindowUnit?: ("days" | "hours") | undefined;
linkedFeatures?: string[] | undefined;
attributeScopeAllProjects?: boolean | undefined;
hasVisualChangesets?: boolean | undefined;
hasURLRedirects?: boolean | undefined;
customFields?: Record<string, any> | undefined;
/** Custom slices that apply to ALL applicable metrics in the experiment */
customMetricSlices?: {
slices: {
column: string;
levels: string[];
}[];
}[] | undefined;
precomputedUnitDimensionIds?: string[] | undefined;
/** ID of the default dashboard for this experiment. */
defaultDashboardId?: string | undefined;
templateId?: string | undefined;
statusUpdateSchedule?: ({
startAt?: string | undefined;
stopAt?: string | undefined;
/** Relative end offset. Deferred: resolved to a concrete `stopAt` at the experiment's actual start (or off `dateStarted` when already running). */
stopAfter?: {
value: number;
unit: "hours" | "days";
} | undefined;
/** What happens at the scheduled end date. `notify` keeps the experiment running and just notifies (soft). `auto-ship` (requires the Decision Framework) ships the winning variation and stops; multi-winner ties break on `tiebreakerMetricId` (higher lift); with no clear winner, `fallback` either keeps running (`notify`) or ships `fallbackVariationId`. `force-ship` stops and rolls out `fallbackVariationId`. `stop` is a hard deadline that stops with no rollout. For `force-ship` and `stop`, the Decision Framework verdict (won/lost/inconclusive) is recorded as metadata when available. */
scheduledStopPlan?: {
mode: "notify" | "auto-ship" | "force-ship" | "stop";
tiebreakerMetricId?: string | undefined;
/** What to do at the scheduled end when there is no clear winner. Required when `mode` is `"auto-ship"`; ignored for other modes. */
fallback?: ("notify" | "force-ship") | undefined;
fallbackVariationId?: string | undefined;
} | undefined;
} | null) | undefined;
nextScheduledStatusUpdate?: ({
type: "start" | "stop";
date: string;
} | null) | undefined;
};
changes?: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
modified: unknown[];
} | undefined;
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.deleted
Triggered when an experiment is deletedPayload
Payload
{
event: "experiment.deleted";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
id: string;
trackingKey: string;
dateCreated: string;
dateUpdated: string;
name: string;
type: "standard" | "multi-armed-bandit" | "holdout";
project: string;
hypothesis: string;
description: string;
tags: string[];
/** The userId of the owner (or raw owner name/email for legacy records) */
owner: string;
ownerEmail?: string | undefined;
archived: boolean;
status: string;
autoRefresh: boolean;
hashAttribute: string;
fallbackAttribute?: string | undefined;
hashVersion: 1 | 2;
disableStickyBucketing?: boolean | undefined;
bucketVersion?: number | undefined;
minBucketVersion?: number | undefined;
variations: {
variationId: string;
key: string;
name: string;
description: string;
screenshots: string[];
}[];
phases: {
name: string;
dateStarted: string;
dateEnded: string;
reasonForStopping: string;
seed: string;
coverage: number;
trafficSplit: {
variationId: string;
weight: number;
}[];
namespace?: {
namespaceId: string;
enabled?: boolean | undefined;
range?: number[] | undefined;
ranges?: [
number,
number
][] | undefined;
} | undefined;
targetingCondition: string;
prerequisites?: {
id: string;
condition: string;
}[] | undefined;
savedGroupTargeting?: {
matchType: "all" | "any" | "none";
savedGroups: string[];
}[] | undefined;
}[];
settings: {
datasourceId: string;
/** The assignment query, grouping its ID with the identifier type analyzed on. */
assignmentQuery: {
/** The ID of one of the data source's assignment queries. */
id: string;
/** The identifier type analyzed on. Null when none can be resolved: no assignment query is selected, or a record saved before identifier types were stored points at a query that no longer exists. */
identifierType: string | null;
};
/** Deprecated: use `assignmentQuery`. */
assignmentQueryId: string;
experimentId: string;
segmentId: string;
queryFilter: string;
inProgressConversions: "include" | "exclude";
/** Setting attribution model to `"experimentDuration"` is the same as selecting "Ignore Conversion Windows" for the Conversion Window Override. Setting it to `"lookbackOverride"` requires a `lookbackOverride` object to be provided. */
attributionModel: "firstExposure" | "experimentDuration" | "lookbackOverride";
/** Controls the lookback override for the experiment. For type "window", value must be a non-negative number and valueUnit is required. */
lookbackOverride?: {
type: "date" | "window";
/** For "window" type - non-negative numeric value (e.g. 7 for 7 days). For "date" type a date string. */
value: number | string;
/** Used when type is "window". Defaults to "days". */
valueUnit?: ("minutes" | "hours" | "days" | "weeks") | undefined;
} | undefined;
statsEngine: "bayesian" | "frequentist";
regressionAdjustmentEnabled?: boolean | undefined;
sequentialTestingEnabled?: boolean | undefined;
sequentialTestingTuningParameter?: number | undefined;
/** When null, the organization default is used. */
postStratificationEnabled?: (boolean | null) | undefined;
/** Controls the decision framework and metric overrides for the experiment. Replaces the entire stored object on update (does not patch individual fields). */
decisionFrameworkSettings?: {
decisionCriteriaId?: string | undefined;
decisionFrameworkMetricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
/** The target relative MDE to use for the metric, expressed as proportions (e.g. use 0.1 for 10%). Must be greater than 0. */
targetMDE?: number | undefined;
}[] | undefined;
} | undefined;
/** Per-metric analysis overrides; also reflected in goals/secondaryMetrics/guardrails overrides when applicable. On create/update, this replaces the entire stored array (it does not patch individual entries). */
metricOverrides?: {
/** ID of the metric to override settings for. */
id: string;
windowType?: ("conversion" | "lookback" | "") | undefined;
windowHours?: number | undefined;
delayHours?: number | undefined;
/** Must be true for the override to take effect. If true, the other proper prior settings in this object will be used if present. */
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
/** Must be true for the override to take effect. If true, the other regression adjustment settings in this object will be used if present. */
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
}[] | undefined;
goals: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
secondaryMetrics: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
guardrails: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
}[];
activationMetric?: {
metricId: string;
overrides: {
delayHours?: number | undefined;
windowHours?: number | undefined;
window?: ("conversion" | "lookback" | "") | undefined;
winRiskThreshold?: number | undefined;
loseRiskThreshold?: number | undefined;
properPriorOverride?: boolean | undefined;
properPriorEnabled?: boolean | undefined;
properPriorMean?: number | undefined;
properPriorStdDev?: number | undefined;
regressionAdjustmentOverride?: boolean | undefined;
regressionAdjustmentEnabled?: boolean | undefined;
regressionAdjustmentDays?: number | undefined;
};
} | undefined;
};
resultSummary?: {
status: string;
winner: string;
conclusions: string;
releasedVariationId: string;
excludeFromPayload: boolean;
} | undefined;
shareLevel?: ("public" | "organization") | undefined;
publicUrl?: string | undefined;
banditScheduleValue?: number | undefined;
banditScheduleUnit?: ("days" | "hours") | undefined;
banditBurnInValue?: number | undefined;
banditBurnInUnit?: ("days" | "hours") | undefined;
banditConversionWindowValue?: number | undefined;
banditConversionWindowUnit?: ("days" | "hours") | undefined;
linkedFeatures?: string[] | undefined;
attributeScopeAllProjects?: boolean | undefined;
hasVisualChangesets?: boolean | undefined;
hasURLRedirects?: boolean | undefined;
customFields?: Record<string, any> | undefined;
/** Custom slices that apply to ALL applicable metrics in the experiment */
customMetricSlices?: {
slices: {
column: string;
levels: string[];
}[];
}[] | undefined;
precomputedUnitDimensionIds?: string[] | undefined;
/** ID of the default dashboard for this experiment. */
defaultDashboardId?: string | undefined;
templateId?: string | undefined;
statusUpdateSchedule?: ({
startAt?: string | undefined;
stopAt?: string | undefined;
/** Relative end offset. Deferred: resolved to a concrete `stopAt` at the experiment's actual start (or off `dateStarted` when already running). */
stopAfter?: {
value: number;
unit: "hours" | "days";
} | undefined;
/** What happens at the scheduled end date. `notify` keeps the experiment running and just notifies (soft). `auto-ship` (requires the Decision Framework) ships the winning variation and stops; multi-winner ties break on `tiebreakerMetricId` (higher lift); with no clear winner, `fallback` either keeps running (`notify`) or ships `fallbackVariationId`. `force-ship` stops and rolls out `fallbackVariationId`. `stop` is a hard deadline that stops with no rollout. For `force-ship` and `stop`, the Decision Framework verdict (won/lost/inconclusive) is recorded as metadata when available. */
scheduledStopPlan?: {
mode: "notify" | "auto-ship" | "force-ship" | "stop";
tiebreakerMetricId?: string | undefined;
/** What to do at the scheduled end when there is no clear winner. Required when `mode` is `"auto-ship"`; ignored for other modes. */
fallback?: ("notify" | "force-ship") | undefined;
fallbackVariationId?: string | undefined;
} | undefined;
} | null) | undefined;
nextScheduledStatusUpdate?: ({
type: "start" | "stop";
date: string;
} | null) | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.warning
Triggered when a warning condition is detected on an experiment, such as a sample ratio mismatch, multiple exposures, low power, no data, or a failed results update. Thetype field identifies the condition.
Payload
Payload
{
event: "experiment.warning";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "auto-update";
success: boolean;
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
} | {
type: "update-failed";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
cause: "query" | "analysis" | "no-queries" | "unknown";
} | {
type: "multiple-exposures";
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
usersCount: number;
percent: number;
} | {
type: "srm";
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
threshold: number;
pValue?: number | undefined;
variations?: {
name: string;
users: number;
weight: number;
}[] | undefined;
durationDays?: number | undefined;
} | {
type: "no-data";
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
} | {
type: "scheduled-status-update-failed";
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
scheduledStatusUpdateType: "start" | "stop";
attempts: number;
maxAttempts: number;
willRetry: boolean;
reason: string;
} | {
type: "underpowered";
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.status.started
Triggered when an experiment starts or resumes running.Payload
Payload
{
event: "experiment.status.started";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "started";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
phaseName?: string | undefined;
goalMetricNames?: string[] | undefined;
linkedFeatureCount?: number | undefined;
visualChangesetCount?: number | undefined;
urlRedirectCount?: number | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.status.stopped
Triggered when an experiment stops, including its result and any temporary rollout.Payload
Payload
{
event: "experiment.status.stopped";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "stopped";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
results?: ("dnf" | "won" | "lost" | "inconclusive") | undefined;
releasedVariationName?: string | undefined;
enableTemporaryRollout: boolean;
reason?: string | undefined;
winningVariationName?: string | undefined;
winningVariationIndex?: number | undefined;
totalUsers?: number | undefined;
durationDays?: number | undefined;
goalMetric?: {
metricId: string;
metricName: string;
inverse?: boolean | undefined;
snapshotId: string;
statsEngine: "bayesian" | "frequentist";
pValueThreshold?: number | undefined;
differenceType: string;
control: {
variationId: string;
variationName: string;
users?: number | undefined;
value: number;
};
variations: {
variationId: string;
variationName: string;
variationIndex: number;
users?: number | undefined;
value: number;
uplift?: number | undefined;
upliftStddev?: number | undefined;
ci?: [
number,
number
] | undefined;
chanceToWin?: number | undefined;
pValue?: number | undefined;
significant?: boolean | undefined;
}[];
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.status.endingSoon
Triggered when a running experiment is within 3 days of its scheduled end date.Payload
Payload
{
event: "experiment.status.endingSoon";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "ending-soon";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
endsAt: string;
daysRemaining: number;
durationDays?: number | undefined;
totalUsers?: number | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.status.stale
Triggered when a running experiment has been active for a long time without a decision.Payload
Payload
{
event: "experiment.status.stale";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "stale";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
daysRunning: number;
totalUsers?: number | undefined;
reason: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.guardrailFailed
Triggered when a running experiment has a failing guardrail metric.Payload
Payload
{
event: "experiment.guardrailFailed";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "guardrail-failed";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
failedMetrics: {
id: string;
name: string;
variationName: string;
}[];
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.bandit.weightsChanged
Triggered when a multi-armed bandit materially changes variation weights.Payload
Payload
{
event: "experiment.bandit.weightsChanged";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
type: "bandit-weights-changed";
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
currentWeights: number[];
updatedWeights: number[];
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.info.significance
Triggered when a goal or guardrail metric reaches significance in an experiment (e.g. either above 95% or below 5% chance to win). Be careful using this without Sequential Testing as it can lead to peeking problems.Payload
Payload
{
event: "experiment.info.significance";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
variationId: string;
variationName: string;
metricName: string;
metricId: string;
statsEngine: string;
criticalValue: number;
winning: boolean;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.info.scheduled-status-update
Triggered when a scheduled start or stop is automatically applied to an experiment, including the auto-ship outcome for a scheduled end.Payload
Payload
{
event: "experiment.info.scheduled-status-update";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
experimentId: string;
experimentName: string;
ownerEmail?: string | undefined;
action: "started" | "stopped" | "kept-running";
shipped?: boolean | undefined;
shippedVariationId?: string | undefined;
shippedVariationName?: string | undefined;
forced?: boolean | undefined;
recommendedVariationId?: string | undefined;
recommendedVariationName?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.decision.ship
Triggered when an experiment is ready to ship a variation.Payload
Payload
{
event: "experiment.decision.ship";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
decisionDescription?: string | undefined;
source: "scheduled-end" | "analysis";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.decision.rollback
Triggered when an experiment should be rolled back to the control.Payload
Payload
{
event: "experiment.decision.rollback";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
decisionDescription?: string | undefined;
source: "scheduled-end" | "analysis";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
experiment.decision.review
Triggered when an experiment has reached the desired power point, but the results may be ambiguous.Payload
Payload
{
event: "experiment.decision.review";
object: "experiment";
api_version: string;
created: number;
data: {
object: {
experimentName: string;
ownerEmail?: string | undefined;
experimentId: string;
decisionDescription?: string | undefined;
source: "scheduled-end" | "analysis";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
holdout.created
Triggered when a holdout is created.Payload
Payload
{
event: "holdout.created";
object: "holdout";
api_version: string;
created: number;
data: {
object: {
holdoutId: string;
holdoutName: string;
ownerEmail?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
holdout.status.changed
Triggered when a holdout moves between its lifecycle stages, such as running or analysis.Payload
Payload
{
event: "holdout.status.changed";
object: "holdout";
api_version: string;
created: number;
data: {
object: {
holdoutId: string;
holdoutName: string;
ownerEmail?: string | undefined;
previousStatus: "draft" | "running" | "analysis-period" | "stopped";
currentStatus: "draft" | "running" | "analysis-period" | "stopped";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
holdout.config.newLinkage
Triggered when feature flags or experiments are linked to a holdout.Payload
Payload
{
event: "holdout.config.newLinkage";
object: "holdout";
api_version: string;
created: number;
data: {
object: {
holdoutId: string;
holdoutName: string;
ownerEmail?: string | undefined;
featureIds: string[];
experimentIds: string[];
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
sdkConnection.payloadSize.warning
Triggered when an SDK Connection’s payload grows past half, 80%, or all of the 16 MB its cache can store. Thelevel field is warning, danger, or over-limit, and recommendations lists ways to shrink it. Fires once per level reached, not on every publish.
Payload
Payload
{
event: "sdkConnection.payloadSize.warning";
object: "sdkConnection";
api_version: string;
created: number;
data: {
object: {
connectionId: string;
connectionName: string;
environment: string;
projects: string[];
level: "warning" | "danger" | "over-limit";
bytes: number;
limitBytes: number;
message: string;
recommendations: {
type: string;
message: string;
}[];
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.created
Triggered when a saved group is createdPayload
Payload
{
event: "savedGroup.created";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.updated
Triggered when a saved group is updatedPayload
Payload
{
event: "savedGroup.updated";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
previous_attributes: {
id?: string | undefined;
type?: ("condition" | "list") | undefined;
dateCreated?: string | undefined;
dateUpdated?: string | undefined;
name?: string | undefined;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
changes?: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
modified: unknown[];
} | undefined;
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.deleted
Triggered when a saved group is deletedPayload
Payload
{
event: "savedGroup.deleted";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.created
Triggered when a new draft revision is created for a saved groupPayload
Payload
{
event: "savedGroup.revision.created";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.updated
Triggered when a draft revision’s proposed changes are modified (values, condition, archive, or metadata). Thechange field indicates the kind of mutation.
Payload
Payload
{
event: "savedGroup.revision.updated";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
change: "metadata" | "condition" | "values" | "archive";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.reviewRequested
Triggered when a draft revision is submitted for reviewPayload
Payload
{
event: "savedGroup.revision.reviewRequested";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.approved
Triggered when a draft revision is approved by a reviewerPayload
Payload
{
event: "savedGroup.revision.approved";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.changesRequested
Triggered when a reviewer requests changes on a draft revisionPayload
Payload
{
event: "savedGroup.revision.changesRequested";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.commented
Triggered when a comment is added to a draft revisionPayload
Payload
{
event: "savedGroup.revision.commented";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.discarded
Triggered when a draft revision is discardedPayload
Payload
{
event: "savedGroup.revision.discarded";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.rebased
Triggered when a draft revision is rebased onto the latest live statePayload
Payload
{
event: "savedGroup.revision.rebased";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.published
Triggered when a draft revision is published. Overlaps withsavedGroup.updated but provides revision-specific context.
Payload
Payload
{
event: "savedGroup.revision.published";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.reverted
Triggered when a saved group is reverted to a previous published revisionPayload
Payload
{
event: "savedGroup.revision.reverted";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
revertedToVersion?: number | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.reopened
Triggered when a discarded revision is reopenedPayload
Payload
{
event: "savedGroup.revision.reopened";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.recalled
Triggered when the author (or an editor) recalls a review request, returning the revision todraft. Distinct from revision.reopened, which restores a discarded revision.
Payload
Payload
{
event: "savedGroup.revision.recalled";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.reviewRetracted
Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end uppending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched.
Payload
Payload
{
event: "savedGroup.revision.reviewRetracted";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.publishScheduleChanged
Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change.Payload
Payload
{
event: "savedGroup.revision.publishScheduleChanged";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
savedGroup.revision.publishFailed
Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve.Payload
Payload
{
event: "savedGroup.revision.publishFailed";
object: "savedGroup";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
savedGroup: {
id: string;
name: string;
};
baseSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
proposedSavedGroup: {
id: string;
type: "condition" | "list";
dateCreated: string;
dateUpdated: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** When type = 'condition', this is the JSON-encoded condition for the group */
condition?: string | undefined;
/** When type = 'list', this is the attribute key the group is based on */
attributeKey?: string | undefined;
/** When type = 'list', this is the list of values for the attribute key */
values?: string[] | undefined;
description?: string | undefined;
projects?: string[] | undefined;
archived?: boolean | undefined;
useEmptyListGroup?: boolean | undefined;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
failureReason: string;
terminal: boolean;
attempts: number;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.created
Triggered when a constant is createdPayload
Payload
{
event: "constant.created";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.updated
Triggered when a constant is updatedPayload
Payload
{
event: "constant.updated";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
previous_attributes: {
id?: string | undefined;
/** Stable reference handle; used as `@const:key` in values */
key?: string | undefined;
name?: string | undefined;
type?: ("string" | "json") | undefined;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated?: string | undefined;
dateUpdated?: string | undefined;
};
changes?: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
modified: unknown[];
} | undefined;
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.deleted
Triggered when a constant is deletedPayload
Payload
{
event: "constant.deleted";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.created
Triggered when a new draft revision is created for a constantPayload
Payload
{
event: "constant.revision.created";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.updated
Triggered when a draft revision’s proposed changes are modified (value, archive, or metadata). Thechange field indicates the kind of mutation.
Payload
Payload
{
event: "constant.revision.updated";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
change: "metadata" | "value" | "archive";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.reviewRequested
Triggered when a draft revision is submitted for reviewPayload
Payload
{
event: "constant.revision.reviewRequested";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.approved
Triggered when a draft revision is approved by a reviewerPayload
Payload
{
event: "constant.revision.approved";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.changesRequested
Triggered when a reviewer requests changes on a draft revisionPayload
Payload
{
event: "constant.revision.changesRequested";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.commented
Triggered when a comment is added to a draft revisionPayload
Payload
{
event: "constant.revision.commented";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.discarded
Triggered when a draft revision is discardedPayload
Payload
{
event: "constant.revision.discarded";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.rebased
Triggered when a draft revision is rebased onto the latest live statePayload
Payload
{
event: "constant.revision.rebased";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.published
Triggered when a draft revision is published. Overlaps withconstant.updated but provides revision-specific context.
Payload
Payload
{
event: "constant.revision.published";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.reverted
Triggered when a constant is reverted to a previous published revisionPayload
Payload
{
event: "constant.revision.reverted";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
revertedToVersion?: number | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.reopened
Triggered when a discarded revision is reopenedPayload
Payload
{
event: "constant.revision.reopened";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.recalled
Triggered when the author (or an editor) recalls a review request, returning the revision todraft. Distinct from revision.reopened, which restores a discarded revision.
Payload
Payload
{
event: "constant.revision.recalled";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.reviewRetracted
Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end uppending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched.
Payload
Payload
{
event: "constant.revision.reviewRetracted";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.publishScheduleChanged
Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change.Payload
Payload
{
event: "constant.revision.publishScheduleChanged";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
constant.revision.publishFailed
Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve.Payload
Payload
{
event: "constant.revision.publishFailed";
object: "constant";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
constant: {
id: string;
key: string;
name: string;
};
baseConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConstant: {
id: string;
/** Stable reference handle; used as `@const:key` in values */
key: string;
name: string;
type: "string" | "json";
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The default value (raw string for `string` constants, JSON-encoded for `json` constants) */
value?: string | undefined;
/** Per-environment value overrides (environment id → value). Falls back to `value` when an environment is absent. */
environmentValues?: Record<string, string> | undefined;
description?: string | undefined;
/** The project this constant belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
failureReason: string;
terminal: boolean;
attempts: number;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.created
Triggered when a config is createdPayload
Payload
{
event: "config.created";
object: "config";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.updated
Triggered when a config is updatedPayload
Payload
{
event: "config.updated";
object: "config";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
previous_attributes: {
id?: string | undefined;
/** Stable reference handle; used as `@config:key` in values */
key?: string | undefined;
name?: string | undefined;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated?: string | undefined;
dateUpdated?: string | undefined;
};
changes?: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
modified: unknown[];
} | undefined;
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.deleted
Triggered when a config is deletedPayload
Payload
{
event: "config.deleted";
object: "config";
api_version: string;
created: number;
data: {
object: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.created
Triggered when a new draft revision is created for a configPayload
Payload
{
event: "config.revision.created";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.updated
Triggered when a draft revision’s proposed changes are modified (value, schema, archive, or metadata). Thechange field indicates the kind of mutation.
Payload
Payload
{
event: "config.revision.updated";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
change: "metadata" | "value" | "schema" | "archive";
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.reviewRequested
Triggered when a draft revision is submitted for reviewPayload
Payload
{
event: "config.revision.reviewRequested";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.approved
Triggered when a draft revision is approved by a reviewerPayload
Payload
{
event: "config.revision.approved";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.changesRequested
Triggered when a reviewer requests changes on a draft revisionPayload
Payload
{
event: "config.revision.changesRequested";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string | null;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.commented
Triggered when a comment is added to a draft revisionPayload
Payload
{
event: "config.revision.commented";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
/** Who performed an action */
reviewer: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
};
reviewComment: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.discarded
Triggered when a draft revision is discardedPayload
Payload
{
event: "config.revision.discarded";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.rebased
Triggered when a draft revision is rebased onto the latest live statePayload
Payload
{
event: "config.revision.rebased";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.published
Triggered when a draft revision is published. Overlaps withconfig.updated but provides revision-specific context.
Payload
Payload
{
event: "config.revision.published";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.reverted
Triggered when a config is reverted to a previous published revisionPayload
Payload
{
event: "config.revision.reverted";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
revertedToVersion?: number | undefined;
bulkPublishId?: string | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.reopened
Triggered when a discarded revision is reopenedPayload
Payload
{
event: "config.revision.reopened";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.recalled
Triggered when the author (or an editor) recalls a review request, returning the revision todraft. Distinct from revision.reopened, which restores a discarded revision.
Payload
Payload
{
event: "config.revision.recalled";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.reviewRetracted
Triggered when a reviewer retracts their own verdict. The status is recomputed from the verdicts that remain, so the revision may end uppending-review, or stay approved or changes-requested when another reviewer’s verdict still stands. Carries no content change — the revision’s proposed changes are untouched.
Payload
Payload
{
event: "config.revision.reviewRetracted";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.publishScheduleChanged
Triggered when a deferred publish is armed, re-armed, or cancelled on a revision. Carries no content change.Payload
Payload
{
event: "config.revision.publishScheduleChanged";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
config.revision.publishFailed
Triggered when a deferred publish (scheduled publish or auto-publish-on-approval) is given up on after failing — terminally, or after exhausting retries. The draft is left open for a human to resolve.Payload
Payload
{
event: "config.revision.publishFailed";
object: "config";
api_version: string;
created: number;
data: {
object: {
id?: string | undefined;
/** 0 for a revision saved before versions were recorded */
version: number;
/** The live version this revision is based on, or 0 if unknown */
baseVersion: number;
status: string;
title?: string | undefined;
/** The revision's description */
comment: string;
dateCreated: string;
dateUpdated: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
authorId: string;
/** Who performed an action */
author?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
autoPublishOnApproval?: boolean | undefined;
autoPublishEnabledBy?: string | undefined;
/** Who performed an action */
autoPublishEnabledByUser?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
scheduledPublishAt?: string | undefined;
scheduledPublishLockEdits?: boolean | undefined;
scheduledPublishLockOthers?: boolean | undefined;
scheduledPublishBypassApproval?: boolean | undefined;
scheduledPublishAttempts?: number | undefined;
scheduledPublishLastError?: string | undefined;
scheduledPublishGaveUpAt?: string | undefined;
config: {
id: string;
key: string;
name: string;
};
baseConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
proposedConfig: {
id: string;
/** Stable reference handle; used as `@config:key` in values */
key: string;
name: string;
/** The userId of the owner (or raw owner name/email for legacy records) */
owner?: string | undefined;
ownerEmail?: string | undefined;
/** The `key` of the config this one inherits from (lineage parent — the primary spine). Synthesized into `$extends` at resolution time and never stored in `value`. */
parent?: string | undefined;
/** Additional composition bases (config `key`s) layered on top of `parent`, in precedence order (later overrides earlier; all override `parent`; this config's own keys win last). Like `parent`, set via this field — never via a `@config:` entry in `value`. */
extends?: string[] | undefined;
/** This config's own base value as a JSON object (its declared fields only — inherited fields are layered in at resolution time, not stored here). Per-environment/project variants are expressed via `scopedOverrides`, not here. */
value?: Record<string, unknown> | undefined;
/** Ordered, first-match-wins environment/project-scoped variant selection. Each entry points at a flavor config (a child config, by `key`) whose value is deep-merged onto this config's resolved value when the (environment, project) scope matches — resolved at build time, per layer. This is how you create an environment-scoped override (as opposed to a plain child config): make a child config for the override value, then add it here with its scope. Send the complete list to replace it; an empty array clears all overrides. Entries must reference existing configs, may not reference this config itself, and may not be unreachable (fully subsumed by an earlier entry). */
scopedOverrides?: {
/** The `key` of the flavor config (a child config) whose value patches this config when the scope matches. */
config: string;
/** Environment ids this entry applies to. Empty/omitted = any environment. */
environments?: string[] | undefined;
/** Project ids this entry applies to. Empty/omitted = any project. */
projects?: string[] | undefined;
}[] | undefined;
/** Present ONLY when this config is an environment/project-scoped override (a "flavor") of another config. Its value is a patch that applies solely within the listed environments/projects, layered onto `parent` at resolution — it is NOT a standalone config. A plain config (including an ordinary child that just inherits from a `parent`) omits this field entirely. Read-only: create/change the relationship via the parent config's `scopedOverrides`, never by setting this directly. */
scopedConfig?: {
/** The base config this one is a scoped override of. */
parent: string;
/** Environments this override applies to (empty/absent = every environment). */
environments?: string[] | undefined;
/** Projects this override applies to (empty/absent = every project). */
projects?: string[] | undefined;
} | undefined;
description?: string | undefined;
/** The project this config belongs to (empty = all projects) */
project?: string | undefined;
archived?: boolean | undefined;
/** This config's own field definitions as a JSON Schema document (its contribution to the family's effective schema). Inherited fields are owned by ancestors and are not repeated here. */
schema?: {
type: "json-schema";
/** A JSON Schema document (an object). */
value: Record<string, unknown>;
} | undefined;
/** Whether this config family permits extra keys beyond the declared fields (child configs, feature rules, ad-hoc overrides). Only the root config's flag applies. Absent = inherit the org default. */
extensible?: boolean | undefined;
/** Cross-field validation rules (relational checks JSON Schema can't express, e.g. implications or comparing two fields), evaluated against the resolved value at publish. */
invariants?: {
/** Unique name for the rule. */
name: string;
/** A mongo condition (mongrule) boolean expression over the config's fields. */
rule: Record<string, unknown>;
/** Human-readable error shown when the rule is violated. */
message: string;
}[] | undefined;
/** Whether this Config is locked to a published revision. Drafts can still be created and edited while locked, but no change can be published until a user with Bypass draft approvals access unlocks it. */
locked?: boolean | undefined;
/** Whether the experiment guard is enabled. When enabled, publishing a value used by a running experiment returns a warning that must be acknowledged with `ignoreWarnings: true`. Disabling the guard requires Bypass draft approvals access. */
experimentGuard?: boolean | undefined;
/** The pinned published revision (present only when `locked`). Fetch it via `GET /configs-revisions/:key/:version` for a value guaranteed not to disappear or mutate — use it to pin reproducible builds. */
lockedRevision?: {
id: string;
version: number;
} | undefined;
/** Who locked the config: a member's user ID, or an API key's ID when it acted as itself */
lockedBy?: string | undefined;
/** When the config was locked (when `locked`). */
dateLocked?: string | undefined;
dateCreated: string;
dateUpdated: string;
};
/** The JSON Patch the draft applies, with each change's value */
proposedChanges: {
op: string;
path: string;
}[];
contributors?: string[] | undefined;
revertedFrom?: string | undefined;
reviews: {
id: string;
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
decision: "approve" | "request-changes" | "comment";
comment?: string | undefined;
stale?: boolean | undefined;
dateCreated: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
}[];
resolution?: {
action: "merged" | "discarded";
/** Who acted: a member's user ID, or an API key's ID when it acted as itself */
userId: string;
/** Who performed an action */
user?: {
/** `dashboard` (signed-in member), `api_key` (API key or personal access token) or `system` */
type: "dashboard" | "api_key" | "system";
/** The member's user ID. Absent for an organization API key */
id?: string | undefined;
/** The API key's ID, when the action came through one */
apiKey?: string | undefined;
/** The member's name, or the organization API key's name */
name?: string | undefined;
/** The member's email. Absent for an organization API key */
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | undefined;
dateCreated: string;
} | undefined;
bulkPublishId?: string | undefined;
failureReason: string;
terminal: boolean;
attempts: number;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
user.login
Triggered when a user logs inPayload
Payload
{
event: "user.login";
object: "user";
api_version: string;
created: number;
data: {
object: {
email: string;
id: string;
name: string;
ip: string;
userAgent: string;
os: string;
device: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
apiKey.expiring
Triggered once when a secret API key is within a week of expiringPayload
Payload
{
event: "apiKey.expiring";
object: "apiKey";
api_version: string;
created: number;
data: {
object: {
id: string;
description?: string | undefined;
expiresAt: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}
apiKey.expired
Triggered once when a secret API key passes its expiration datePayload
Payload
{
event: "apiKey.expired";
object: "apiKey";
api_version: string;
created: number;
data: {
object: {
id: string;
description?: string | undefined;
expiresAt: string;
};
};
user: {
type: "dashboard";
id: string;
email: string;
name: string;
} | {
type: "api_key";
apiKey: string;
id?: string | undefined;
name?: string | undefined;
email?: string | undefined;
/** The member named with `X-GrowthBook-Requested-By` */
requestedBy?: {
id: string;
name: string;
email: string;
} | undefined;
/** Whether the key assumed the named member's role */
assumedRole?: boolean | undefined;
} | {
type: "system";
subtype?: string | undefined;
id?: string | undefined;
} | null;
tags: string[];
/** The environments affected by the change described by this event. For live-state events (e.g. `feature.updated`) these are the environments whose effective configuration actually changed; for draft lifecycle events (`*.revision.*`) they are the environments the proposed changes would affect. Webhook environment filters match against this field. An empty array means the event has no environment-scoped impact (it will only be delivered to subscriptions without an environment filter). */
environments: string[];
containsSecrets: boolean;
}

