---
title: "Pre-Commit Webhooks API Reference"
description: "Reference for public pre-commit webhook payloads, authentication, Change Validation API callbacks, responses, errors, and availability."
product: general
lang: en
token_estimate: 4382
---
# Pre-Commit Webhooks API Reference

This reference documents the public API contract for pre-commit webhooks. Use it with the [Pre-Commit Webhooks Guide](https://docs.statsig.com/guides/pre-commit-webhooks), which explains feature access, configuration, validation lifecycle, and operations.

> For AI agents: a documentation index is available at [/llms.txt](/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

## Availability

Pre-commit webhooks are feature gated at the organization level. Contact your Statsig account team or [Statsig Support](https://docs.statsig.com/support-options) to enable the feature for your organization.

As of September 2026, the Change Validation API accepts edit-validation callbacks and callbacks for Console-initiated creations. Refer to [Availability limitations](#availability-limitations) for creation and recovery API support.

## Authentication and request conventions

### Requests to the Change Validation API

Authenticate requests to Statsig with a Console API key in the `STATSIG-API-KEY` header. Create and manage Console API keys from the [Statsig API keys page](https://console.statsig.com/api_keys).

```bash
STATSIG-API-KEY: console-xxxxxxxxxxxxxxxx
Content-Type: application/json
```

Creation-validation verdicts and progress updates require a write-capable key with permission to modify the project. Edit-validation callbacks accept read-only and write-capable keys.

The Console API applies approximate rate limits of 100 requests per 10 seconds and 900 requests per 15 minutes. Design validators to avoid unnecessary retries and handle rate-limit responses gracefully.

### Requests from Statsig to your webhook

Statsig sends webhook requests to the HTTPS URL configured in the Statsig Console. Requests use `POST` with a JSON body and include the following headers:

| Header | Description |
| --- | --- |
| `Content-Type` | `application/json` |
| `x-statsig-webhook-key` | The webhook key configured for the organization. Use it to authenticate requests from Statsig. |
| `x-statsig-project-name` | The Statsig project name. Statsig sends this header when a webhook key is configured. |

If no webhook key is configured, Statsig omits both custom headers. Configure a webhook key and validate it before processing the request.

The webhook endpoint must return a 2xx response within 2 minutes. This response acknowledges delivery only; it doesn't approve or reject the change. Submit the validation verdict separately through the Change Validation API.

Webhook payload fields use `snake_case`. Change Validation API fields use `camelCase`.

## Webhook payload reference

### Common fields

The following fields appear in the public edit-validation payloads:

| Field | Type | Description |
| --- | --- | --- |
| `review_id` | string | Identifier of the Statsig review being validated. |
| `change_id` | string | Identifier for the proposed configuration change. Present for gate, dynamic config, segment, and holdout payloads. Experiment payloads don't include this field. |
| `submitter` | string | Email address or actor identifier for the user who submitted the change. |
| `committer` | string | Email address or actor identifier for the user committing the change. |
| `config_type` | string | Resource type: `gate`, `dynamic_config`, `segment`, `holdout`, or `experiment`. |
| `config_name` | string | Name of the gate, dynamic config, segment, or holdout. Not used for experiment payloads. |
| `experiment_name` | string | Name of the experiment. Used instead of `config_name` for experiment payloads. |
| `type` | string | Discriminator for the change operation. Refer to the supported variants below. |

### Supported edit variants

| `config_type` | `type` | Payload-specific fields |
| --- | --- | --- |
| `gate`, `dynamic_config`, `segment` | `rules` | `diffs`, `old_config`, `new_config` |
| `gate`, `dynamic_config`, `segment`, `experiment` | `update_target_apps` | `old_target_apps`, `new_target_apps` |
| `holdout` | `holdout_settings` | `old_settings`, `new_settings` |
| `experiment` | `update_allocation` | `old_allocation`, `new_allocation`, `old_experiment` |
| `experiment` | `start_experiment` | `new_allocation`, `old_experiment` |
| `experiment` | `ship_experiment` | `group`, `old_experiment`, `decision_reason` |
| `experiment` | `abandon_experiment` | `old_experiment`, `decision_reason` |
| `experiment` | `update_experiment_settings` | `old_settings`, `new_settings`, `old_experiment`, `new_experiment` |

### Rules changes

`rules` payloads describe changes to feature gates, dynamic configs, or segments.

| Field | Type | Description |
| --- | --- | --- |
| `diffs` | array or `null` | Structured differences between the previous and proposed rules. |
| `old_config` | object, optional in the contract | The resource state before the change. |
| `new_config` | object, optional in the contract | The proposed resource state. |

The nested resource objects use the corresponding Statsig resource representation. The Console API resource definitions maintain their complete schemas, so this reference doesn't duplicate them.

Example, abbreviated:

```json
{
  "review_id": "review_01HXYZ",
  "change_id": "snapshot_01HXYZ",
  "submitter": "submitter@example.com",
  "committer": "committer@example.com",
  "config_type": "gate",
  "config_name": "checkout_gate",
  "type": "rules",
  "diffs": [],
  "old_config": {
    "name": "checkout_gate"
  },
  "new_config": {
    "name": "checkout_gate"
  }
}
```

### Target application changes

`update_target_apps` payloads contain the previous and proposed target application names.

| Field | Type | Description |
| --- | --- | --- |
| `old_target_apps` | array of strings | Target applications before the change. |
| `new_target_apps` | array of strings | Target applications after the change. |

Example, abbreviated:

```json
{
  "review_id": "review_01HXYZ",
  "change_id": "snapshot_01HXYZ",
  "submitter": "submitter@example.com",
  "committer": "committer@example.com",
  "config_type": "dynamic_config",
  "config_name": "checkout_config",
  "type": "update_target_apps",
  "old_target_apps": ["web"],
  "new_target_apps": ["web", "ios"]
}
```

### Holdout setting changes

`holdout_settings` payloads contain the previous and proposed holdout settings.

| Field | Type | Description |
| --- | --- | --- |
| `isGlobal` | boolean | Whether the holdout is global. |
| `globalApplyToGates` | boolean | Whether a global holdout applies to gates. |
| `globalApplyToExperiments` | boolean | Whether a global holdout applies to experiments. |
| `globalApplyToLayers` | boolean | Whether a global holdout applies to layers. |
| `passPercent` | number | Holdout pass percentage. |
| `gates` | array of strings | Names of gates associated with the holdout. |
| `experiments` | array of strings | Names of experiments associated with the holdout. |
| `layers` | array of strings | Names of layers associated with the holdout. |
| `targetingGateName` | string or `null` | Targeting gate name, if configured. |

Both `old_settings` and `new_settings` use this object shape.

### Experiment changes

Experiment payloads use `experiment_name` and don't include `change_id`.

| `type` | Fields | Description |
| --- | --- | --- |
| `update_allocation` | `old_allocation`, `new_allocation`, `old_experiment` | Changes the experiment allocation. |
| `start_experiment` | `new_allocation`, `old_experiment` | Starts an experiment. |
| `ship_experiment` | `group`, `old_experiment`, `decision_reason` | Ships a selected experiment group. |
| `abandon_experiment` | `old_experiment`, `decision_reason` | Abandons an experiment. |
| `update_target_apps` | `old_target_apps`, `new_target_apps` | Changes the experiment's target applications. |
| `update_experiment_settings` | `old_settings`, `new_settings`, `old_experiment`, `new_experiment` | Changes experiment settings. |

The `old_experiment` and `new_experiment` objects use the corresponding Statsig experiment representation. The settings objects contain experiment groups, allocation, targeting gate, and inline targeting rules.

Experiment settings use the following fields:

| Field | Type | Description |
| --- | --- | --- |
| `groups` | array of objects | Experiment groups. Each group contains `name`, `parameterValuesJSON`, and `size`. |
| `allocation` | number | Experiment allocation percentage. |
| `targetingGateID` | string or `null` | Targeting gate identifier, if configured. |
| `inlineTargetingRulesJSON` | string or `null` | Inline targeting rules, if configured. |

Example, abbreviated:

```json
{
  "review_id": "review_01HXYZ",
  "submitter": "submitter@example.com",
  "committer": "committer@example.com",
  "config_type": "experiment",
  "experiment_name": "checkout_experiment",
  "type": "ship_experiment",
  "group": "variant_a",
  "old_experiment": {
    "name": "checkout_experiment"
  },
  "decision_reason": "Variant A met the success criteria."
}
```

## Change Validation API

The Change Validation API records the verdict produced by your validation service. It's a callback endpoint, not a status-retrieval or polling endpoint.

### Creation validation callback requirements

Creation callbacks require `creationValidationAttemptID`; edit callbacks don't.

Each creation-validation retry generates a new `creation_validation_attempt_id`. Use the value from the latest webhook payload so that a callback for an earlier retry can't affect the current validation.

### Submit a validation result

```bash
POST https://statsigapi.net/console/v1/change_validation
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reviewID` | string | Yes | The `review_id` from the webhook payload. |
| `creationValidationAttemptID` | string | For creation callbacks | The `creation_validation_attempt_id` from the latest creation webhook payload. Omit for edit callbacks. |
| `validated` | boolean | Yes | `true` approves the edit or creation; `false` rejects it. |
| `message` | string | No | Feedback displayed to the user. |
| `debugLinks` | array of objects | No | Links to relevant logs or diagnostic information. Each object contains required `title` and `url` strings. |
| `releasePipelineIDForCommit` | string or `null` | No | Release pipeline behavior for an approved gate or dynamic config edit. Omit this field to use the resource's configured behavior; send `null` to explicitly disable a release pipeline. |

Send `releasePipelineIDForCommit` only when approving an edit to a gate or dynamic config. Don't send it for experiments, segments, holdouts, or rejected validations. Statsig doesn't support release pipeline overrides for creation validation.

Statsig records `debugLinks` with an approved validation result. Provide them when `validated` is `true`. Statsig doesn't use them to approve or reject the change.

#### Example: approve a change

```bash
curl --request POST \
  --url https://statsigapi.net/console/v1/change_validation \
  --header 'STATSIG-API-KEY: console-xxxxxxxxxxxxxxxx' \
  --header 'Content-Type: application/json' \
  --data '{
    "reviewID": "review_01HXYZ",
    "validated": true,
    "message": "The change passed policy validation.",
    "debugLinks": [
      {
        "title": "Validation run",
        "url": "https://validator.example.com/runs/01HXYZ"
      }
    ]
  }'
```

#### Example: reject a change

```bash
curl --request POST \
  --url https://statsigapi.net/console/v1/change_validation \
  --header 'STATSIG-API-KEY: console-xxxxxxxxxxxxxxxx' \
  --header 'Content-Type: application/json' \
  --data '{
    "reviewID": "review_01HXYZ",
    "validated": false,
    "message": "The change violates the production rollout policy."
  }'
```

#### Example: approve a creation

Use the values from the creation webhook payload:

```bash
curl --request POST \
  --url https://statsigapi.net/console/v1/change_validation \
  --header 'STATSIG-API-KEY: console-xxxxxxxxxxxxxxxx' \
  --header 'Content-Type: application/json' \
  --data '{
    "reviewID": "review_01HXYZ",
    "creationValidationAttemptID": "attempt_01HXYZ",
    "validated": true,
    "message": "The creation passed policy validation."
  }'
```

To reject the creation, send `validated: false` with the same `reviewID` and current `creationValidationAttemptID`. Statsig keeps the resource held and displays the feedback.

#### Response

Successful requests return HTTP `200`:

```json
{
  "message": "Validation status updated successfully"
}
```

### Update the validation message

Use this endpoint to publish progress or replace the message shown while validation is pending.

```bash
PATCH https://statsigapi.net/console/v1/change_validation/message
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reviewID` | string | Yes | The `review_id` from the webhook payload. |
| `creationValidationAttemptID` | string | For creation callbacks | The `creation_validation_attempt_id` from the latest creation webhook payload. Omit for edit callbacks. |
| `message` | string | No | The current validation progress or status message. |

```bash
curl --request PATCH \
  --url https://statsigapi.net/console/v1/change_validation/message \
  --header 'STATSIG-API-KEY: console-xxxxxxxxxxxxxxxx' \
  --header 'Content-Type: application/json' \
  --data '{
    "reviewID": "review_01HXYZ",
    "message": "Running policy and integration checks."
  }'
```

Example request body for `PATCH /console/v1/change_validation/message` when updating creation-validation progress:

```json
{
  "reviewID": "review_01HXYZ",
  "creationValidationAttemptID": "attempt_01HXYZ",
  "message": "Running creation policy checks."
}
```

Successful requests return HTTP `200`:

```json
{
  "message": "Validation message updated successfully"
}
```

Send message updates only while the validation is pending. The endpoint doesn't approve, reject, or retrieve the validation result.

## Errors and status handling

Console API errors use this general shape:

```json
{
  "status": 400,
  "message": "Invalid review ID"
}
```

The response may also include an `errors` field with validation details.

| HTTP status | Common causes | Recommended action |
| --- | --- | --- |
| `400 Bad Request` | Invalid request body, invalid review ID, review isn't pending, or an invalid release-pipeline combination. | Correct the request and submit it for the active pending review. |
| `401 Unauthorized` | Missing, invalid, or inactive `STATSIG-API-KEY`, or a read-only key used for a creation callback. | Use an active Console API key. Creation verdicts and progress updates require a write-capable key. |
| `403 Forbidden` | The caller lacks access to the requested resource. | Verify the key's project and resource permissions. |
| `409 Conflict` | A creation callback sends a missing, empty, or outdated `creationValidationAttemptID`. | Use `creation_validation_attempt_id` from the latest webhook payload. Don't retry the unchanged callback. |
| `500 Internal Server Error` | Statsig could not complete a server-side operation after accepting the validation result. | Check the response message, avoid blindly repeating requests, and contact Statsig Support if the problem persists. |

The public API doesn't provide a polling endpoint for validation status. Track the `reviewID` in your validator and use the Statsig Console to inspect the resulting status and message.

Creation callbacks distinguish these HTTP `409` messages:

| Message | Action |
| --- | --- |
| `creationValidationAttemptID is required for creation validation callbacks. Set it to the creation_validation_attempt_id from the webhook payload.` | Include `creationValidationAttemptID` in the verdict or progress update. |
| `This callback targets an outdated creation validation attempt. Use the creation_validation_attempt_id from the latest webhook payload.` | Use the latest webhook payload to set `creationValidationAttemptID` in the callback. |

## Availability limitations

### Creation validation API support

As of September 2026, the public API contract doesn't support creation-validation payload schemas. Use the Statsig Console UI for creation validation; public API support for initiating creation validation isn't available yet. Refer to the [main guide](https://docs.statsig.com/guides/pre-commit-webhooks) for the creation-validation lifecycle and contact Statsig for current availability.

### Creation-validation recovery

As of September 2026, you can use retry, discard, and bypass recovery actions for creation validation only in the Statsig Console. Statsig excludes the corresponding internal routes from the public OpenAPI specification. Don't use them as public API integrations. Public creation and recovery API support remains unreleased. Public Console API support is planned for a future release. Disabling creation validation stops new validations but doesn't release resources already held for validation.

### Configuration management

The public API reference doesn't include an endpoint for configuring the webhook URL, webhook key, or initial message. Configure these values in the Statsig Console under _Settings > Product Configuration > General_.

### Resource coverage

The public edit-validation payload contract covers gates, dynamic configs, segments, holdouts, and experiments. As of September 2026, the public API contract doesn't include layer creation payloads or other creation-validation payloads.

