{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/foundational/json-schema/foundational-public-policy-response-schema.json",
  "title": "PublicPolicyResponse",
  "description": "A policy as returned by the public API: a workflow, authored here or in the app.\n\nRead responses surface every action type a workflow can carry, including ones the narrower\n``PublicPolicyActions`` request shape does not accept. Policies managed by data contracts are\nnot part of this surface.",
  "x-generated": "2026-10-29",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/foundational-openapi.json#/components/schemas/PublicPolicyResponse",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "title": "Id",
      "description": "Stable identifier of the policy."
    },
    "name": {
      "type": "string",
      "title": "Name",
      "description": "Policy name (unique within the organization)."
    },
    "isEnabled": {
      "type": "boolean",
      "title": "Isenabled",
      "description": "Whether the policy currently fires."
    },
    "triggeringEvents": {
      "items": {
        "$ref": "#/$defs/PolicyTriggeringEvent"
      },
      "type": "array",
      "title": "Triggeringevents",
      "description": "Events that cause the policy to evaluate."
    },
    "rulesData": {
      "$ref": "#/$defs/WorkflowRulesData",
      "description": "Rules that decide when the policy fires."
    },
    "actionsData": {
      "$ref": "#/$defs/PolicyActionsData",
      "description": "Actions taken when the policy fires. A `send_webhook` action's `url` is shown as its scheme and host only (`https://hooks.example.com`), since a webhook URL often carries its receiver's credential."
    },
    "repositoryNames": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "title": "Repositorynames",
      "description": "Repository full names this policy is scoped to. Empty list means it applies to every repository."
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "title": "Createdat",
      "description": "When the policy was created (UTC)."
    },
    "updatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Updatedat",
      "description": "When the policy was last updated (UTC). `null` if it has never been updated."
    }
  },
  "type": "object",
  "required": [
    "id",
    "name",
    "isEnabled",
    "triggeringEvents",
    "rulesData",
    "actionsData",
    "repositoryNames",
    "createdAt"
  ],
  "$defs": {
    "AddPRReviewerAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "add_pr_reviewer",
          "title": "Action Type",
          "default": "add_pr_reviewer"
        },
        "reviewers": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Reviewers"
        }
      },
      "type": "object",
      "required": [
        "reviewers"
      ],
      "title": "AddPRReviewerAction"
    },
    "ChangeAffectingEntitiesQueryRule": {
      "properties": {
        "rule_type": {
          "type": "string",
          "const": "change_affecting_entities_query_rule",
          "title": "Rule Type",
          "default": "change_affecting_entities_query_rule"
        },
        "matching_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Matching Entity Queries",
          "default": []
        },
        "upstream_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Upstream Entity Queries",
          "default": []
        }
      },
      "additionalProperties": false,
      "type": "object",
      "title": "ChangeAffectingEntitiesQueryRule",
      "description": "A rule that triggers when a change lands on a watched entity or anywhere in its upstream.\n\nA *subject* watch: ``matching_entity_queries`` names the entities the user is protecting, so a\nchange inside a matched entity -- a column of a watched table -- counts as a change affecting it,\nand is reported against the matched ancestor the user configured.\n\nThe rule is the union of two arms, either of which fires it:\n\n* the **self** arm, a change of any type (added, removed or modified) on a watched entity or\n  inside one. Never narrowed by ``upstream_entity_queries``: a filter naming where a change may\n  come *from* has nothing to say about a change on the subject itself.\n* the **upstream** arm, a change of any type on something the watched entities transitively\n  depend on, optionally narrowed to ``upstream_entity_queries``.\n\nThe two arms are genuinely disjoint rather than one subsuming the other, which is why both are\nneeded: the upstream traversal excludes the watched entities themselves, and the self arm does\nnot follow lineage.\n\n``matching_entity_queries`` is required non-empty -- the upstream arm resolves it against the\nwhole org, so an empty list is an unbounded watch rather than an absent filter.\n``upstream_entity_queries`` is satisfied when the changed entity itself matches; ``[]``\nimposes no constraint. An added-or-removed change on a watched entity also satisfies\n``entity_added_or_removed_query_rule``; a policy holding both types firing twice on one change is\naccepted rather than deduplicated."
    },
    "EntityAddedOrRemovedQueryRule": {
      "properties": {
        "rule_type": {
          "type": "string",
          "const": "entity_added_or_removed_query_rule",
          "title": "Rule Type",
          "default": "entity_added_or_removed_query_rule"
        },
        "matching_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Matching Entity Queries",
          "default": []
        },
        "change_types": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/PolicyRuleChangeType"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Change Types"
        }
      },
      "additionalProperties": false,
      "type": "object",
      "title": "EntityAddedOrRemovedQueryRule",
      "description": "A rule that triggers when an entity matching the rule's queries is added or removed.\n\n``change_types`` selects which of ``ENTITY_ADDED_OR_REMOVED_CHANGE_TYPES`` trigger the rule.\n``None`` and ``[]`` both mean \"every supported change type\" -- an empty selection is normalized\nto ``None`` on construction, so a consumer only ever sees ``None`` or a non-empty list. That\nmatches the editor's All/Added/Removed selector, where \"All\" is the absence of a filter rather\nthan a selection of everything. The entity kind is expressed as a ``type:`` token inside the\nquery strings, not as a field on the rule.\n\nThis rule names *the changes to report*, not a subject to protect, so a child of a matched entity\nis reported only when it matches ``matching_entity_queries`` itself (FOU-16832). The rule that\nreads a watch as a subject -- and so follows a change down into it -- is\n``change_affecting_entities_query_rule``."
    },
    "IssueSeverity": {
      "type": "string",
      "enum": [
        "critical",
        "error",
        "warning",
        "info"
      ],
      "title": "IssueSeverity"
    },
    "NewDownstreamConsumersQueryRule": {
      "properties": {
        "rule_type": {
          "type": "string",
          "const": "new_downstream_consumers_query_rule",
          "title": "Rule Type",
          "default": "new_downstream_consumers_query_rule"
        },
        "matching_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Matching Entity Queries",
          "default": []
        },
        "downstream_consumer_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Downstream Consumer Queries",
          "default": []
        }
      },
      "additionalProperties": false,
      "type": "object",
      "title": "NewDownstreamConsumersQueryRule",
      "description": "A rule that triggers when something new starts reading from a watched entity.\n\nKeyed on lineage *edges* added by the change, not on entities: a new edge between two\npre-existing entities -- a query that starts reading another table -- fires the rule, and an\nentity added without any edge cannot. A watched entity counts as the edge's source when the\nsource is the watched entity itself, is transitively upstream of it, or is a column of it.\n\n``matching_entity_queries`` is required non-empty: unlike its issue- and diff-bounded siblings\nthis rule has no candidate set to fall back on, so an empty watch list makes it permanently inert\nrather than unconstrained.\n\n``downstream_consumer_queries`` limits which new consumers count -- the edge's destination, or\nits parent, must match one of the queries. ``[]`` imposes no constraint, which is exactly the\nbehaviour the rule had before the field existed."
    },
    "NewIssueQueryRule": {
      "properties": {
        "rule_type": {
          "type": "string",
          "const": "new_issue_query_rule",
          "title": "Rule Type",
          "default": "new_issue_query_rule"
        },
        "matching_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Matching Entity Queries",
          "default": []
        },
        "downstream_entity_queries": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Downstream Entity Queries",
          "default": []
        },
        "issue_types": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Issue Types",
          "default": [
            "*"
          ]
        },
        "issue_severities": {
          "items": {
            "$ref": "#/$defs/IssueSeverity"
          },
          "type": "array",
          "title": "Issue Severities",
          "default": []
        }
      },
      "additionalProperties": false,
      "type": "object",
      "title": "NewIssueQueryRule",
      "description": "A rule that triggers when a new issue is detected on an entity matching the rule's queries.\n\n``issue_severities`` is a membership filter, not a minimum: an empty list matches every severity.\n``issue_types`` is the same kind of filter: an empty list, or any list containing ``\"*\"`` (the\ndefault), matches every issue type; anything else is exact membership -- a \"deselect all\" in the\nUI must not silently produce a rule that can never fire.\n\nA selection covering the whole issue-type catalog, or a whole category of it, is stored in a\nform that keeps up with the catalog, so a type added later fires the rule without it being\nre-saved. Reading such a rule back therefore reports the types that qualify *now*, which can be\nmore (or differently ordered) than what was submitted.\n\nThat form reserves the ``category:`` prefix, so entries starting with it are rejected on write.\nAn entry naming no known issue type is otherwise accepted, and matches nothing.\n\n``downstream_entity_queries`` narrows the rule to watched entities that reach one of the queries\n-- the entity itself, its parent, or one of its transitive downstreams matches a query; ``[]``\nimposes no such constraint. It is the one query list this category resolves *leniently*: with no\nlineage graph it is ignored when ``matching_entity_queries`` is also set, since it only narrows\nan already-bounded candidate set. Without ``matching_entity_queries`` it is the sole entity\nscoping and fails closed. An entity-free issue type (``entity_free_issue_type()`` returns\n``True``) skips both constraints, so types that can never carry an entity still reach a rule that\nasks for one; entity-capable types with unresolved required descriptors fail closed.\n\nThis is the only rule type carrying ``downstream_entity_queries``. Whether a given issue type\ncan be filtered by it at all is a per-category affordance the ``/workflows/issue_types`` surface\npublishes as ``supports_downstream_filter``."
    },
    "PolicyActionsData": {
      "properties": {
        "actions": {
          "items": {
            "oneOf": [
              {
                "$ref": "#/$defs/SendEmailAction"
              },
              {
                "$ref": "#/$defs/SendSlackMessageAction"
              },
              {
                "$ref": "#/$defs/SendTeamsMessageAction"
              },
              {
                "$ref": "#/$defs/AddPRReviewerAction"
              },
              {
                "$ref": "#/$defs/SubmitPRReviewAction"
              },
              {
                "$ref": "#/$defs/SendWebhookAction"
              }
            ]
          },
          "type": "array",
          "title": "Actions"
        }
      },
      "type": "object",
      "required": [
        "actions"
      ],
      "title": "PolicyActionsData"
    },
    "PolicyRuleChangeType": {
      "type": "string",
      "enum": [
        "added",
        "removed",
        "modified",
        "renamed"
      ],
      "title": "PolicyRuleChangeType",
      "description": "The type of the lineage entity change (eg. added / removed / modified / renamed)"
    },
    "PolicyTriggeringEvent": {
      "type": "string",
      "enum": [
        "*",
        "on_pr_in_progress",
        "on_pr_merged",
        "on_non_code_scanned",
        "on_pushed_to_default_branch",
        "on_observability_scan"
      ],
      "title": "PolicyTriggeringEvent"
    },
    "SendEmailAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "send_email",
          "title": "Action Type",
          "default": "send_email"
        },
        "recipients": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Recipients"
        }
      },
      "type": "object",
      "required": [
        "recipients"
      ],
      "title": "SendEmailAction"
    },
    "SendSlackMessageAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "send_slack_message",
          "title": "Action Type",
          "default": "send_slack_message"
        },
        "channels": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Channels"
        }
      },
      "type": "object",
      "required": [
        "channels"
      ],
      "title": "SendSlackMessageAction"
    },
    "SendTeamsMessageAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "send_teams_message",
          "title": "Action Type",
          "default": "send_teams_message"
        },
        "channels": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Channels"
        }
      },
      "type": "object",
      "required": [
        "channels"
      ],
      "title": "SendTeamsMessageAction"
    },
    "SendWebhookAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "send_webhook",
          "title": "Action Type",
          "default": "send_webhook"
        },
        "url": {
          "type": "string",
          "title": "Url"
        }
      },
      "type": "object",
      "required": [
        "url"
      ],
      "title": "SendWebhookAction",
      "description": "POST the trigger payload to an HTTPS endpoint when the policy fires."
    },
    "SubmitPRReviewAction": {
      "properties": {
        "action_type": {
          "type": "string",
          "const": "submit_pr_review",
          "title": "Action Type",
          "default": "submit_pr_review"
        }
      },
      "type": "object",
      "title": "SubmitPRReviewAction",
      "description": "Submit a PR review on every evaluation: REQUEST_CHANGES when the policy triggers,\nAPPROVE when it does not."
    },
    "WorkflowRulesData": {
      "properties": {
        "conjunctive_evaluation": {
          "type": "boolean",
          "title": "Conjunctive Evaluation"
        },
        "rules": {
          "items": {
            "oneOf": [
              {
                "$ref": "#/$defs/NewIssueQueryRule"
              },
              {
                "$ref": "#/$defs/EntityAddedOrRemovedQueryRule"
              },
              {
                "$ref": "#/$defs/ChangeAffectingEntitiesQueryRule"
              },
              {
                "$ref": "#/$defs/NewDownstreamConsumersQueryRule"
              }
            ]
          },
          "type": "array",
          "minItems": 1,
          "title": "Rules"
        }
      },
      "type": "object",
      "required": [
        "conjunctive_evaluation",
        "rules"
      ],
      "title": "WorkflowRulesData",
      "description": "``PolicyRulesData`` restricted to the query-targeted rule types — the shape the workflows API exposes.\n\n``rules`` must be non-empty: a rule-less workflow could never fire."
    }
  }
}