---
title: "Create a draft policy version"
method: POST
path: "/api/policy_version"
tags: ["Policy Versions"]
---

# Create a draft policy version

`POST /api/policy_version`

## Request

- Base URL: `https://app.herondata.io`
- URL: `https://app.herondata.io/api/policy_version`
- Auth: API key in header `x-api-key`

Create a new draft cloned from the current active version's policy_definition, or a blank definition if there is no active version.

## Query parameters

- `user_id` integer

## Request body

- PolicyVersionCreateSchema
  - `change_description` string, nullable

## Response `201`

OK

- object
  - `policy_version` PolicyVersionSchema
    - `author_email` string: Email of whoever last acted on this version: the drafter while it is a draft, the publisher once published
    - `backtest_status` PolicyVersionBacktestStatus
      - `backtest_heron_id` string, nullable
      - `deal_count` integer, nullable: Deals the backtest covered
      - `finished_at` string, date-time, nullable
      - `flipped_deal_count` integer, nullable: Deals whose outcome changed, null unless the run completed
      - `run_status` unknown
      - `stale` boolean: The version was edited after that backtest was asked for, so re-run before publishing
    - `change_description` string, nullable: What changed in this version
    - `created` string, date-time
    - `last_edited_at` string, date-time, nullable
    - `policy_definition` PolicyDefinition
      - `policy_sections` PolicyVersionSection[], required
        - `branches` PolicyBranch[]: Ordered routes through the section; the section's own checks run on every route
          - `checks` Check[]
            - `attribute_description` string, nullable: What the attribute measures. Present only for accounts on policy versions
            - `attribute_display_name` string, nullable: The attribute's human-readable name. Present only for accounts on policy versions
            - `attribute_label` string, nullable
            - `attribute_unit` unknown
            - `attribute_validators` object[], nullable: The validity operators `attribute_label` offers, as in the catalogue's `check_options[].validators`, so a saved `is_valid` / `is_invalid` check can be worded. Empty for a check without an attribute or an attribute with no validity rule
              - …
            - `attribute_value_set` unknown
            - `boolean_check` BooleanCheck
              - …
            - `composite_check` CompositeCheck
              - …
            - `date_check` DateCheck
              - …
            - `date_delta_to_now_check` DateDeltaToNowCheck
              - …
            - `enhanced_sql_check` EnhancedSQLCheck
              - …
            - `group` string, nullable
            - `heron_id` string, nullable: Stable across edits; omit for a new check and one is minted. Policy versions only
            - `identity` string, nullable: Stable kind:key identity used to match stored check evaluations
            - `name` string, nullable
            - `numeric_check` NumericCheck
              - …
            - `parameters` object, nullable
            - `sql_check` SQLCheck
              - …
            - `string_check` StringCheck
              - …
          - `condition` Check, required
            - `attribute_description` string, nullable: What the attribute measures. Present only for accounts on policy versions
            - `attribute_display_name` string, nullable: The attribute's human-readable name. Present only for accounts on policy versions
            - `attribute_label` string, nullable
            - `attribute_unit` unknown
            - `attribute_validators` object[], nullable: The validity operators `attribute_label` offers, as in the catalogue's `check_options[].validators`, so a saved `is_valid` / `is_invalid` check can be worded. Empty for a check without an attribute or an attribute with no validity rule
              - …
            - `attribute_value_set` unknown
            - `boolean_check` BooleanCheck
              - …
            - `composite_check` CompositeCheck
              - …
            - `date_check` DateCheck
              - …
            - `date_delta_to_now_check` DateDeltaToNowCheck
              - …
            - `enhanced_sql_check` EnhancedSQLCheck
              - …
            - `group` string, nullable
            - `heron_id` string, nullable: Stable across edits; omit for a new check and one is minted. Policy versions only
            - `identity` string, nullable: Stable kind:key identity used to match stored check evaluations
            - `name` string, nullable
            - `numeric_check` NumericCheck
              - …
            - `parameters` object, nullable
            - `sql_check` SQLCheck
              - …
            - `string_check` StringCheck
              - …
          - `heron_id` string, nullable: Stable across renames; omit for a new branch and one is minted
          - `name` string, required
        - `checks` Check[]
          - `attribute_description` string, nullable: What the attribute measures. Present only for accounts on policy versions
          - `attribute_display_name` string, nullable: The attribute's human-readable name. Present only for accounts on policy versions
          - `attribute_label` string, nullable
          - `attribute_unit` unknown
          - `attribute_validators` object[], nullable: The validity operators `attribute_label` offers, as in the catalogue's `check_options[].validators`, so a saved `is_valid` / `is_invalid` check can be worded. Empty for a check without an attribute or an attribute with no validity rule
          - `attribute_value_set` unknown
          - `boolean_check` BooleanCheck
            - `auto_decline` boolean
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `transaction_scope` TransactionScope
              - …
            - `value` boolean
          - `composite_check` CompositeCheck
            - `auto_decline` boolean
            - `checks` Check[]
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `max_failing` integer, nullable
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `transaction_scope` TransactionScope
              - …
            - `type` 'AND' | 'OR' | 'AT_MOST_FAILING'
          - `date_check` DateCheck
            - `auto_decline` boolean
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `transaction_scope` TransactionScope
              - …
            - `type` 'equal' | 'not_equal' | 'before' | 'before_or_on' | 'after' | 'after_or_on'
            - `value` string, date, nullable
          - `date_delta_to_now_check` DateDeltaToNowCheck
            - `auto_decline` boolean
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `pass_if_value_missing` boolean
            - `transaction_scope` TransactionScope
              - …
            - `type` 'greater_than' | 'greater_than_or_equal' | 'less_than' | 'less_than_or_equal'
            - `value` object, nullable
          - `enhanced_sql_check` EnhancedSQLCheck
            - `auto_decline` boolean
            - `check_type` 'equal' | 'not_equal' | 'greater_than' | 'greater_than_or_equal' | 'less_than' | 'less_than_or_equal' | 'before' | 'before_or_on' | 'after' | 'after_or_on' | 'is_in' | 'is_not_in', required
            - `column_name` string, required
            - `comparison_value` unknown, required
            - `data_type` 'numeric' | 'date' | 'string', required
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `query_name` string, required
            - `transaction_scope` TransactionScope
              - …
          - `group` string, nullable
          - `heron_id` string, nullable: Stable across edits; omit for a new check and one is minted. Policy versions only
          - `identity` string, nullable: Stable kind:key identity used to match stored check evaluations
          - `name` string, nullable
          - `numeric_check` NumericCheck
            - `auto_decline` boolean
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `transaction_scope` TransactionScope
              - …
            - `type` 'equal' | 'not_equal' | 'greater_than' | 'greater_than_or_equal' | 'less_than' | 'less_than_or_equal'
            - `value` union: The threshold: a number as a string, {"variable": {...}} to compare with another variable (with an optional "factor" that scales it), or {"lookup": {"table_heron_id", "column_id"}} to read it from a lookup table column
              - …
          - `parameters` object, nullable
          - `sql_check` SQLCheck
            - `auto_decline` boolean
            - `column_name` string, required
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `query_name` string, required
            - `transaction_scope` TransactionScope
              - …
          - `string_check` StringCheck
            - `auto_decline` boolean
            - `decline_code` string, nullable: The CRM's identifier for this decline reason, e.g. a LendSaas reason id. Left unset, the CRM sync falls back to this check's decline_description.
            - `decline_description` string, nullable
            - `email_template_name` string, nullable
            - `ignore_metric_availability` boolean
            - `is_mandatory` boolean
            - `match_count` integer, nullable
            - `on_failure` 'fail' | 'review_required'
            - `on_missing` 'missing_data' | 'fail' | 'pass': What the check reports when it would report missing data: `missing_data` (the default), `fail` or `pass`. A check saved with an older flag (`pass_if_value_missing` on a date-delta check) reads as the setting it stands for. With `ignore_metric_availability` or `pass_if_value_missing` set, only that setting is accepted
            - `pass_description` string, nullable
            - `transaction_scope` TransactionScope
              - …
            - `type` 'equal' | 'not_equal' | 'starts_with' | 'starts_with_any_of' | 'does_not_start_with_any_of' | 'contains' | 'does_not_start_with' | 'does_not_contain' | 'is_in' | 'is_not_in' | 'has_none_of' | 'has_any_of' | 'has_only' | 'has_fewer_than_n_matches' | 'has_more_than_n_matches' | 'is_valid' | 'is_invalid'
            - `value` union: The value as a string, or {"lookup": {"table_heron_id"}} for is_in, is_not_in, has_any_of, has_none_of or has_only to read the list from a lookup table's rows
              - …
        - `default_branch` DefaultPolicyBranch
          - `checks` Check[]
            - `attribute_description` string, nullable: What the attribute measures. Present only for accounts on policy versions
            - `attribute_display_name` string, nullable: The attribute's human-readable name. Present only for accounts on policy versions
            - `attribute_label` string, nullable
            - `attribute_unit` unknown
            - `attribute_validators` object[], nullable: The validity operators `attribute_label` offers, as in the catalogue's `check_options[].validators`, so a saved `is_valid` / `is_invalid` check can be worded. Empty for a check without an attribute or an attribute with no validity rule
              - …
            - `attribute_value_set` unknown
            - `boolean_check` BooleanCheck
              - …
            - `composite_check` CompositeCheck
              - …
            - `date_check` DateCheck
              - …
            - `date_delta_to_now_check` DateDeltaToNowCheck
              - …
            - `enhanced_sql_check` EnhancedSQLCheck
              - …
            - `group` string, nullable
            - `heron_id` string, nullable: Stable across edits; omit for a new check and one is minted. Policy versions only
            - `identity` string, nullable: Stable kind:key identity used to match stored check evaluations
            - `name` string, nullable
            - `numeric_check` NumericCheck
              - …
            - `parameters` object, nullable
            - `sql_check` SQLCheck
              - …
            - `string_check` StringCheck
              - …
          - `condition` unknown
          - `decline_code` string, nullable: Optional code sent with a failed or review_required result
          - `decline_description` string, nullable: Required when result is failed or review_required
          - `heron_id` string, nullable
          - `name` string
          - `result` unknown
        - `display_order` integer, nullable
        - `heron_id` string, nullable
        - `name` string, required
        - `use_case` 'background_check' | 'cashflow'
    - `published_at` string, date-time, nullable
    - `status` 'draft' | 'active' | 'archived'
    - `version` integer: Version number, unique per account

## Other responses

- `404`: Not found
- `422`: A draft already exists, or policy versions changed concurrently. When a draft exists, `errors` holds one entry with code `draft_exists` and the open draft's `draft_version`, `draft_author_email` and `draft_last_edited_at`. Nothing is changed: publish or delete that draft, or check with its author, before starting new changes. An automated sync should stop and tell someone rather than edit and publish a draft it didn't open.

## Changes

- **2026-11-03** `1ec936e17fef`: 4 warning
  - added the new `industry_category` enum value to the `policy_version/policy_definition/allOf[#/components/schemas/PolicyDefinition]/policy_sections/items/branches/items/checks/items/attribute_value_set` response property for the response status `201`
  - added the new `industry_category` enum value to the `policy_version/policy_definition/allOf[#/components/schemas/PolicyDefinition]/policy_sections/items/branches/items/condition/allOf[#/components/schemas/Check]/attribute_value_set` response property for the response status `201`
  - added the new `industry_category` enum value to the `policy_version/policy_definition/allOf[#/components/schemas/PolicyDefinition]/policy_sections/items/checks/items/attribute_value_set` response property for the response status `201`
  - added the new `industry_category` enum value to the `policy_version/policy_definition/allOf[#/components/schemas/PolicyDefinition]/policy_sections/items/default_branch/allOf[#/components/schemas/DefaultPolicyBranch]/checks/items/attribute_value_set` response property for the response status `201`
- **2026-11-03** `bd8008e49534`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/herondata/apis/heron-data-api/changes/api/policy_version/post.md)

---

[API](https://skmtc.dev/herondata/apis/heron-data-api.md) - [All operations](https://skmtc.dev/herondata/apis/heron-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/herondata/apis/heron-data-api/revisions/1ec936e17fef?raw)
