---
title: "Publish the draft policy version"
method: POST
path: "/api/policy_version/publish"
tags: ["Policy Versions"]
---

# Publish the draft policy version

`POST /api/policy_version/publish`

## Request

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

Make the draft the active version. The whole draft goes live, including any edits made in the dashboard, not only values set through the API. The previously active version is archived, and author_email becomes the publisher's email. Refused when a check reads data that no workflow path produces before its section's policy node.

## Query parameters

- `user_id` integer

## Response `200`

OK

- object
  - `issues` PublishIssueSchema[]: Warnings that did not stop the publish
    - `attribute_label` string, nullable: The variable the check reads
    - `available_after` string, nullable: Customer-facing name of the step after which every deal has the variable
    - `blocks_publish` boolean, required: Publishing is refused while this is true; saving never is
    - `check_name` string, nullable: The top-level check, even when a condition in it reads the variable
    - `code` 'data_on_no_path' | 'data_not_in_workflow' | 'data_on_some_paths_only' | 'data_unverified' | 'policy_node_left_without_sections' | 'unknown_attribute' | 'invalid_attribute_parameters' | 'incompatible_compared_variable' | 'unreadable_lookup_threshold' | 'email_address_compared_with_domain', required
    - `message` string, required
    - `path` unknown[], required: The check that reads the variable, as a save error would point at it, down to a condition inside a group; empty for a workflow node
      - unknown
    - `policy_node_id` string, nullable: Workflow node left with no sections
    - `section_heron_id` string, nullable
    - `section_name` string, nullable
  - `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`: No draft policy version
- `422`: A concurrent change won the race, or the draft has issues that block publishing, listed in `errors`.

## 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 `200`
  - 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 `200`
  - 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 `200`
  - 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 `200`
- **2026-11-03** `bd8008e49534`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/herondata/apis/heron-data-api/changes/api/policy_version/publish/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)
