---
title: "Create a policy"
method: POST
path: "/v1/workspaces/{workspaceId}/policies"
---

# Create a policy

`POST /v1/workspaces/{workspaceId}/policies`

## Path parameters

- `workspaceId` string, required

## Request body

- CreatePolicyRequest
  - `description` string
  - `enabled` boolean
  - `metadata` object — Arbitrary metadata for the policy (record<string, string>)
  - `name` string, required
  - `priority` integer
  - `rules` CreatePolicyRule[]
    - `anyApproval` AnyApprovalRule
      - `minApprovals` integer, required
    - `deploymentDependency` DeploymentDependencyRule
      - `dependsOn` string, required — CEL expression to match upstream deployment(s) that must have a successful release before this deployment can proceed.
    - `deploymentWindow` DeploymentWindowRule
      - `allowWindow` boolean, required — If true, deployments are only allowed during the window. If false, deployments are blocked during the window (deny window)
      - `durationMinutes` integer, required — Duration of each deployment window in minutes
      - `rrule` string, required — RFC 5545 recurrence rule defining when deployment windows start (e.g., FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9)
      - `timezone` string — IANA timezone for the rrule (e.g., America/New_York). Defaults to UTC if not specified
    - `environmentProgression` EnvironmentProgressionRule
      - `dependsOnEnvironmentSelector` union, required
        - JsonSelector
          - `json` object, required
        - CelSelector
          - `cel` string, required
      - `maximumAgeHours` integer — Maximum age of dependency deployment before blocking progression (prevents stale promotions)
      - `minimumSockTimeMinutes` integer — Minimum time to wait after the depends on environment is in a success state before the current environment can be deployed
      - `minimumSuccessPercentage` number, float
      - `successStatuses` JobStatus[]
    - `gradualRollout` GradualRolloutRule
      - `rolloutType` 'linear' | 'linear-normalized', required — Strategy for scheduling deployments to release targets. "linear": Each target is deployed at a fixed interval of timeScaleInterval seconds. "linear-normalized": Deployments are spaced evenly so that the last target is scheduled at or before timeScaleInterval seconds. See rolloutType algorithm documentation for details.
      - `timeScaleInterval` integer, required — Base time interval in seconds used to compute the delay between deployments to release targets.
    - `retry` RetryRule
      - `backoffSeconds` integer — Minimum seconds to wait between retry attempts. If null, retries are allowed immediately after job completion.
      - `backoffStrategy` 'linear' | 'exponential' — Backoff strategy: "linear" uses constant backoffSeconds delay, "exponential" doubles the delay with each retry (backoffSeconds * 2^(attempt-1)).
      - `maxBackoffSeconds` integer — Maximum backoff time in seconds (cap for exponential backoff). If null, no maximum is enforced.
      - `maxRetries` integer, required — Maximum number of retries allowed. 0 means no retries (1 attempt total), 3 means up to 4 attempts (1 initial + 3 retries).
      - `retryOnStatuses` JobStatus[] — Job statuses that count toward the retry limit. If null or empty, defaults to ["failure", "invalidIntegration", "invalidJobAgent"] for maxRetries > 0, or ["failure", "invalidIntegration", "invalidJobAgent", "successful"] for maxRetries = 0. Cancelled and skipped jobs never count by default (allows redeployment after cancellation). Example: ["failure", "cancelled"] will only count failed/cancelled jobs.
    - `verification` VerificationRule
      - `metrics` VerificationMetricSpec[], required — Metrics to verify
        - `count` integer, required — Number of measurements to take
        - `failureCondition` string — CEL expression to evaluate measurement failure (e.g., "result.statusCode == 500"), if not provided, a failure is just the opposite of the success condition
        - `failureThreshold` integer — Stop after this many consecutive failures (0 = no limit)
        - `intervalSeconds` integer, required — Interval between measurements in seconds
        - `name` string, required — Name of the verification metric
        - `provider` union, required
          - HTTPMetricProvider
            - `body` string — Request body (supports Go templates)
            - `headers` object — HTTP headers (values support Go templates)
            - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' — HTTP method
            - `timeout` string — Request timeout (duration string, e.g., "30s")
            - `type` 'http', required — Provider type
            - `url` string, required — HTTP endpoint URL (supports Go templates)
          - SleepMetricProvider
            - `durationSeconds` integer, required
            - `type` 'sleep', required — Provider type
          - DatadogMetricProvider
            - `aggregator` 'avg' | 'min' | 'max' | 'sum' | 'last' | 'percentile' | 'mean' | 'l2norm' | 'area' — Datadog aggregator
            - `apiKey` string, required — Datadog API key (supports Go templates for variable references)
            - `appKey` string, required — Datadog Application key (supports Go templates for variable references)
            - `formula` string — Datadog formula (supports Go templates)
            - `intervalSeconds` integer
            - `queries` object, required — Datadog metrics queries (supports Go templates)
            - `site` string — Datadog site URL (e.g., datadoghq.com, datadoghq.eu, us3.datadoghq.com)
            - `type` 'datadog', required — Provider type
          - PrometheusMetricProvider
            - `address` string, required — Prometheus server address (supports Go templates)
            - `authentication` object — Authentication configuration for Prometheus
              - …
            - `headers` object[] — Additional HTTP headers for the Prometheus request (values support Go templates)
              - …
            - `insecure` boolean — Skip TLS certificate verification
            - `query` string, required — PromQL query expression (supports Go templates)
            - `rangeQuery` object — If provided, a range query (/api/v1/query_range) is used instead of an instant query (/api/v1/query)
              - …
            - `timeout` integer — Query timeout in seconds
            - `type` 'prometheus', required — Provider type
          - TerraformCloudRunMetricProvider
            - `address` string, required — Terraform Cloud address
            - `runId` string, required — Terraform Cloud run ID
            - `token` string, required — Terraform Cloud token
            - `type` 'terraformCloudRun', required — Provider type
        - `successCondition` string, required — CEL expression to evaluate measurement success (e.g., "result.statusCode == 200")
        - `successThreshold` integer — Minimum number of consecutive successful measurements required to consider the metric successful
      - `triggerOn` 'jobCreated' | 'jobStarted' | 'jobSuccess' | 'jobFailure' — When to trigger verification
    - `versionCooldown` VersionCooldownRule
      - `intervalSeconds` integer, required — Minimum time in seconds that must pass since the currently deployed (or in-progress) version was created before allowing another deployment. This enables batching of frequent upstream releases into periodic deployments.
    - `versionSelector` VersionSelectorRule
      - `description` string — Human-readable description of what this version selector does. Example: "Only deploy v2.x versions to staging environments"
      - `selector` union, required
        - JsonSelector
          - `json` object, required
        - CelSelector
          - `cel` string, required
  - `selector` string — CEL expression for matching release targets. Use "true" to match all targets.

## Response `202`

Accepted response

- Policy
  - `createdAt` string, required
  - `description` string
  - `enabled` boolean, required
  - `id` string, required
  - `metadata` object, required — Arbitrary metadata for the policy (record<string, string>)
  - `name` string, required
  - `priority` integer, required
  - `rules` PolicyRule[], required
    - `anyApproval` AnyApprovalRule
      - `minApprovals` integer, required
    - `createdAt` string, required
    - `deploymentDependency` DeploymentDependencyRule
      - `dependsOn` string, required — CEL expression to match upstream deployment(s) that must have a successful release before this deployment can proceed.
    - `deploymentWindow` DeploymentWindowRule
      - `allowWindow` boolean, required — If true, deployments are only allowed during the window. If false, deployments are blocked during the window (deny window)
      - `durationMinutes` integer, required — Duration of each deployment window in minutes
      - `rrule` string, required — RFC 5545 recurrence rule defining when deployment windows start (e.g., FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9)
      - `timezone` string — IANA timezone for the rrule (e.g., America/New_York). Defaults to UTC if not specified
    - `environmentProgression` EnvironmentProgressionRule
      - `dependsOnEnvironmentSelector` union, required
        - JsonSelector
          - `json` object, required
        - CelSelector
          - `cel` string, required
      - `maximumAgeHours` integer — Maximum age of dependency deployment before blocking progression (prevents stale promotions)
      - `minimumSockTimeMinutes` integer — Minimum time to wait after the depends on environment is in a success state before the current environment can be deployed
      - `minimumSuccessPercentage` number, float
      - `successStatuses` JobStatus[]
    - `gradualRollout` GradualRolloutRule
      - `rolloutType` 'linear' | 'linear-normalized', required — Strategy for scheduling deployments to release targets. "linear": Each target is deployed at a fixed interval of timeScaleInterval seconds. "linear-normalized": Deployments are spaced evenly so that the last target is scheduled at or before timeScaleInterval seconds. See rolloutType algorithm documentation for details.
      - `timeScaleInterval` integer, required — Base time interval in seconds used to compute the delay between deployments to release targets.
    - `id` string, required
    - `policyId` string, required
    - `retry` RetryRule
      - `backoffSeconds` integer — Minimum seconds to wait between retry attempts. If null, retries are allowed immediately after job completion.
      - `backoffStrategy` 'linear' | 'exponential' — Backoff strategy: "linear" uses constant backoffSeconds delay, "exponential" doubles the delay with each retry (backoffSeconds * 2^(attempt-1)).
      - `maxBackoffSeconds` integer — Maximum backoff time in seconds (cap for exponential backoff). If null, no maximum is enforced.
      - `maxRetries` integer, required — Maximum number of retries allowed. 0 means no retries (1 attempt total), 3 means up to 4 attempts (1 initial + 3 retries).
      - `retryOnStatuses` JobStatus[] — Job statuses that count toward the retry limit. If null or empty, defaults to ["failure", "invalidIntegration", "invalidJobAgent"] for maxRetries > 0, or ["failure", "invalidIntegration", "invalidJobAgent", "successful"] for maxRetries = 0. Cancelled and skipped jobs never count by default (allows redeployment after cancellation). Example: ["failure", "cancelled"] will only count failed/cancelled jobs.
    - `verification` VerificationRule
      - `metrics` VerificationMetricSpec[], required — Metrics to verify
        - `count` integer, required — Number of measurements to take
        - `failureCondition` string — CEL expression to evaluate measurement failure (e.g., "result.statusCode == 500"), if not provided, a failure is just the opposite of the success condition
        - `failureThreshold` integer — Stop after this many consecutive failures (0 = no limit)
        - `intervalSeconds` integer, required — Interval between measurements in seconds
        - `name` string, required — Name of the verification metric
        - `provider` union, required
          - HTTPMetricProvider
            - `body` string — Request body (supports Go templates)
            - `headers` object — HTTP headers (values support Go templates)
            - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' — HTTP method
            - `timeout` string — Request timeout (duration string, e.g., "30s")
            - `type` 'http', required — Provider type
            - `url` string, required — HTTP endpoint URL (supports Go templates)
          - SleepMetricProvider
            - `durationSeconds` integer, required
            - `type` 'sleep', required — Provider type
          - DatadogMetricProvider
            - `aggregator` 'avg' | 'min' | 'max' | 'sum' | 'last' | 'percentile' | 'mean' | 'l2norm' | 'area' — Datadog aggregator
            - `apiKey` string, required — Datadog API key (supports Go templates for variable references)
            - `appKey` string, required — Datadog Application key (supports Go templates for variable references)
            - `formula` string — Datadog formula (supports Go templates)
            - `intervalSeconds` integer
            - `queries` object, required — Datadog metrics queries (supports Go templates)
            - `site` string — Datadog site URL (e.g., datadoghq.com, datadoghq.eu, us3.datadoghq.com)
            - `type` 'datadog', required — Provider type
          - PrometheusMetricProvider
            - `address` string, required — Prometheus server address (supports Go templates)
            - `authentication` object — Authentication configuration for Prometheus
              - …
            - `headers` object[] — Additional HTTP headers for the Prometheus request (values support Go templates)
              - …
            - `insecure` boolean — Skip TLS certificate verification
            - `query` string, required — PromQL query expression (supports Go templates)
            - `rangeQuery` object — If provided, a range query (/api/v1/query_range) is used instead of an instant query (/api/v1/query)
              - …
            - `timeout` integer — Query timeout in seconds
            - `type` 'prometheus', required — Provider type
          - TerraformCloudRunMetricProvider
            - `address` string, required — Terraform Cloud address
            - `runId` string, required — Terraform Cloud run ID
            - `token` string, required — Terraform Cloud token
            - `type` 'terraformCloudRun', required — Provider type
        - `successCondition` string, required — CEL expression to evaluate measurement success (e.g., "result.statusCode == 200")
        - `successThreshold` integer — Minimum number of consecutive successful measurements required to consider the metric successful
      - `triggerOn` 'jobCreated' | 'jobStarted' | 'jobSuccess' | 'jobFailure' — When to trigger verification
    - `versionCooldown` VersionCooldownRule
      - `intervalSeconds` integer, required — Minimum time in seconds that must pass since the currently deployed (or in-progress) version was created before allowing another deployment. This enables batching of frequent upstream releases into periodic deployments.
    - `versionSelector` VersionSelectorRule
      - `description` string — Human-readable description of what this version selector does. Example: "Only deploy v2.x versions to staging environments"
      - `selector` union, required
        - JsonSelector
          - `json` object, required
        - CelSelector
          - `cel` string, required
  - `selector` string, required — CEL expression for matching release targets. Use "true" to match all targets.
  - `workspaceId` string, required

## Other responses

- `400` — Invalid request

## Changes

- **2026-02-20** `39388a4a4db2` — 3 warning
  - removed the request property `rules/items/createdAt`
  - removed the request property `rules/items/id`
  - removed the request property `rules/items/policyId`
- **2026-02-20** `b9cba5428947` — 2 info
  - added the new optional request property `rules/items/versionSelector`
  - added the optional property `rules/items/versionSelector` to the response with the `202` status
- **2026-02-19** `e61389fc98ac` — 8 breaking, 8 warning, 7 info
  - added the new required request property `rules/items/createdAt`
  - added the new required request property `rules/items/id`
  - added the new required request property `rules/items/policyId`
  - removed the enum value `queued` of the request property `rules/items/environmentProgression/successStatuses/items/`
  - …19 more

[Change history](https://skmtc.dev/ctrlplanedev/apis/ctrlplane-api/changes/v1/workspaces/:workspaceId/policies/post.md)

---

[API](https://skmtc.dev/ctrlplanedev/apis/ctrlplane-api.md) · [All operations](https://skmtc.dev/ctrlplanedev/apis/ctrlplane-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/ctrlplanedev/ctrlplane-api/revisions/69370c50cf4e/schema)
