{
  "openapi": "3.1.0",
  "info": {
    "title": "Scytale API",
    "description": "Read-only access to your Scytale compliance data.\n\nAuthenticate by exchanging your client credentials at `POST /oauth/token` for an\naccess token, then send it as `Authorization: Bearer <token>`.\n",
    "version": "0.1.0"
  },
  "paths": {
    "/v1/controls": {
      "get": {
        "tags": [
          "controls"
        ],
        "summary": "List controls",
        "description": "Return controls matching the filters, one page at a time.\n\nEach row is a full control: code, name, description, owner, framework criteria,\nframework (id and name), applicability, audit and the monitors feeding it. Filters\ncombine with AND.\n\n`pagination.total` is every control that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated.",
        "operationId": "get_controls",
        "parameters": [
          {
            "name": "applicable",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "description": "True for only applicable controls, false for only inapplicable. Omit for both - note the dashboard counts applicable controls only, so pass true to match its totals.",
              "title": "Applicable"
            },
            "description": "True for only applicable controls, false for only inapplicable. Omit for both - note the dashboard counts applicable controls only, so pass true to match its totals."
          },
          {
            "name": "auditId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only controls under this audit (its id).",
              "title": "Auditid"
            },
            "description": "Only controls under this audit (its id)."
          },
          {
            "name": "ownerId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only controls owned by this user, by exact user id (not a name).",
              "title": "Ownerid"
            },
            "description": "Only controls owned by this user, by exact user id (not a name)."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "const": "rank",
              "type": "string",
              "description": "Order of the results. Only 'rank' (the product's display order).",
              "default": "rank",
              "title": "Sort"
            },
            "description": "Order of the results. Only 'rank' (the product's display order)."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Sort descending instead of ascending.",
              "default": false,
              "title": "Descending"
            },
            "description": "Sort descending instead of ascending."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Control_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/controls/{id}": {
      "get": {
        "tags": [
          "controls"
        ],
        "summary": "Get control by ID",
        "description": "Return one control by id - the same shape as a list row.\n\nUse it to follow up on a specific control after `get_controls`, or when you already\nhold an id.",
        "operationId": "get_control",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The control's id, as returned by get_controls.",
              "title": "Id"
            },
            "description": "The control's id, as returned by get_controls."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Control"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such control for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/audits": {
      "get": {
        "tags": [
          "audits"
        ],
        "summary": "List audits",
        "description": "Return the company's audits, one page at a time, newest audit period first.\n\nEach row is an audit engagement: the framework being audited (id and name), its\nlifecycle status (`active`, `not_active` or `completed`), the audit period and\ntimestamps. Every audit is listed, whatever its status - narrow with `status` to\nanswer \"what is our current audit\". Filters combine with AND.\n\n`pagination.total` is every audit that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results.",
        "operationId": "get_audits",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/AuditStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only audits in this lifecycle status. Omit for all statuses.",
              "title": "Status"
            },
            "description": "Only audits in this lifecycle status. Omit for all statuses."
          },
          {
            "name": "frameworkId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only audits of this framework (its id).",
              "title": "Frameworkid"
            },
            "description": "Only audits of this framework (its id)."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "startDate",
                "endDate",
                "createdAt",
                "updatedAt"
              ],
              "type": "string",
              "description": "Field to order the results by. Audits with no value for it come last when descending.",
              "default": "startDate",
              "title": "Sort"
            },
            "description": "Field to order the results by. Audits with no value for it come last when descending."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Newest first (the default); false for oldest first.",
              "default": true,
              "title": "Descending"
            },
            "description": "Newest first (the default); false for oldest first."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Audit_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/audits/{id}": {
      "get": {
        "tags": [
          "audits"
        ],
        "summary": "Get audit by ID",
        "description": "Return one audit by id: a list row plus `product`, the product the audit is\nscoped to when the framework is certified per product (null otherwise).\n\nUse it to follow up on a specific audit after `get_audits`, or to resolve the\n`auditId` on a control.",
        "operationId": "get_audit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The audit's id, as returned by get_audits.",
              "title": "Id"
            },
            "description": "The audit's id, as returned by get_audits."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such audit for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/policies": {
      "get": {
        "tags": [
          "policies"
        ],
        "summary": "List policies",
        "description": "Return policies matching the filters, one page at a time.\n\nEach row carries the policy's identity, its owner and approver, both statuses, and\nwhen its sign-off was given and lapses. **Metadata only - the policy document itself\nis never returned.** Filters combine with AND.\n\n`state` answers \"is this signed off and still valid\"; `workflowStatus` answers \"where\nis it in its review cycle\". They can disagree: a policy whose sign-off expired is\n`approval-required` with `signedOffAt` still set.\n\n`pagination.total` is every policy that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated.",
        "operationId": "get_policies",
        "parameters": [
          {
            "name": "ownerId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only policies owned by this user, by exact user id (not a name).",
              "title": "Ownerid"
            },
            "description": "Only policies owned by this user, by exact user id (not a name)."
          },
          {
            "name": "approverId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only policies this user must approve, by exact user id. Combine with `workflowStatus=pending-approval` for what is actually waiting on them.",
              "title": "Approverid"
            },
            "description": "Only policies this user must approve, by exact user id. Combine with `workflowStatus=pending-approval` for what is actually waiting on them."
          },
          {
            "name": "isExternalPolicy",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "description": "True for only externally managed policies, false for only internal. Omit for both.",
              "title": "Isexternalpolicy"
            },
            "description": "True for only externally managed policies, false for only internal. Omit for both."
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PolicyState"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only policies in this compliance state.",
              "title": "State"
            },
            "description": "Only policies in this compliance state."
          },
          {
            "name": "workflowStatus",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PolicyWorkflowStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only policies at this point in the review cycle.",
              "title": "Workflowstatus"
            },
            "description": "Only policies at this point in the review cycle."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "code",
                "title",
                "updatedAt"
              ],
              "type": "string",
              "description": "Order of the results.",
              "default": "code",
              "title": "Sort"
            },
            "description": "Order of the results."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Sort descending instead of ascending.",
              "default": false,
              "title": "Descending"
            },
            "description": "Sort descending instead of ascending."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Policy_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/policies/{id}": {
      "get": {
        "tags": [
          "policies"
        ],
        "summary": "Get policy by ID",
        "description": "Return one policy by id - a list row plus this cycle's reviewers.\n\nUse it to follow up after `get_policies`, or when you already hold an id, to see who\nwas asked to review and which of them have finished.",
        "operationId": "get_policy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The policy's id, as returned by get_policies.",
              "title": "Id"
            },
            "description": "The policy's id, as returned by get_policies."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such policy for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/monitors": {
      "get": {
        "tags": [
          "monitors"
        ],
        "summary": "List monitors",
        "description": "Return monitors - the automated and manual checks feeding controls - one page at a time.\n\nEach row carries the monitor's identity, its `state`, the integrations feeding it,\nwhen it last ran, its owner, and how many controls it feeds (`controlCount`). The\ncontrols themselves are on `get_monitor`: \"which controls are affected by our failing\nchecks\" is `state=non-compliant` here, then `get_monitor` on each of those few rows -\nnot a walk of this list. Filters combine with AND.\n\n`state` is the verdict of the latest automated evidence and has three values, not\ntwo: `non-compliant` when a check failed since the monitor was last reviewed,\n`compliant` when checks ran and none is failing, `pending` when no automated evidence\nhas been collected - a manual monitor, or an integration that has not run. `pending`\nis a third answer, not a failure: do not count it as either. \"Which checks are\nfailing\" is `state=non-compliant`; \"which checks have never produced a verdict\" is\n`state=pending`; \"when did our AWS checks last run\" is `lastRunAt` on the rows whose\n`integrationNames` start with `aws-`.\n\n`pagination.total` is every monitor that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated.",
        "operationId": "get_monitors",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/MonitorState"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only monitors in this state. Three values, not two: non-compliant (a check failed since the last review), compliant (checks ran and none is failing), pending (no automated evidence has been collected, so there is no verdict either way - a manual monitor, or an integration that has not run).",
              "title": "State"
            },
            "description": "Only monitors in this state. Three values, not two: non-compliant (a check failed since the last review), compliant (checks ran and none is failing), pending (no automated evidence has been collected, so there is no verdict either way - a manual monitor, or an integration that has not run)."
          },
          {
            "name": "ownerId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only monitors owned by this user, by exact user id (not a name).",
              "title": "Ownerid"
            },
            "description": "Only monitors owned by this user, by exact user id (not a name)."
          },
          {
            "name": "frequency",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/MonitorFrequency"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only monitors meant to run at this frequency.",
              "title": "Frequency"
            },
            "description": "Only monitors meant to run at this frequency."
          },
          {
            "name": "inactive",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "description": "True for only switched-off monitors, false for only active ones. Omit for both.",
              "title": "Inactive"
            },
            "description": "True for only switched-off monitors, false for only active ones. Omit for both."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "code",
                "name",
                "updatedAt",
                "lastRunAt"
              ],
              "type": "string",
              "description": "Order of the results.",
              "default": "code",
              "title": "Sort"
            },
            "description": "Order of the results."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Sort descending instead of ascending.",
              "default": false,
              "title": "Descending"
            },
            "description": "Sort descending instead of ascending."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Monitor_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/monitors/{id}": {
      "get": {
        "tags": [
          "monitors"
        ],
        "summary": "Get monitor by ID",
        "description": "Return one monitor by id - a list row plus `controls`, the controls it feeds.\n\nUse it to follow up on a specific monitor after `get_monitors`, or from a control's\n`monitorIds`, to see what the check is, which of the three states it is in, when it\nlast ran and which controls a failure affects.",
        "operationId": "get_monitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The monitor's id, as returned by get_monitors.",
              "title": "Id"
            },
            "description": "The monitor's id, as returned by get_monitors."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such monitor for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/vendors": {
      "get": {
        "tags": [
          "vendors"
        ],
        "summary": "List vendors",
        "description": "Return vendors - the third parties the company tracks for third-party risk - one page at a time.\n\nEach row is the whole vendor: identity (`name`, `description`, `website`, `category`),\nhow much the business depends on it (`criticality`), where the relationship stands\n(`lifeCycle`), its risk ratings (`businessRisk` and the confidentiality, integrity and\navailability risks), what data it handles (`dataClassification`, `sensitiveDataTypes`),\nthe due diligence on file (`certifications`, `dpaOnFile`, `signedContract`,\n`penetrationTestConducted`), and the review cycle (`reviewFrequency`, `lastReviewDate`,\n`nextReviewDate`, `ownerId`). A vendor added from Scytale's catalog shows the catalog's\nvalues wherever the company recorded none, so a row is never missing its name.\n`get_vendor` returns the same shape; there is nothing more on the detail.\n\nFilters combine with AND. \"Which vendors do we currently use\" is `lifeCycle=active`;\n\"our high-risk vendors\" is `businessRisk=high`; \"which vendors have never been\nreviewed\" is the rows with a null `lastReviewDate`; \"which reviews are overdue\" is\n`sort=nextReviewDate&descending=false`, reading rows whose `nextReviewDate` is in the\npast; \"which vendors are missing a DPA\" is the rows whose `dpaOnFile` is false or null.\n\n`pagination.total` is every vendor that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated.",
        "operationId": "get_vendors",
        "parameters": [
          {
            "name": "criticality",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VendorCriticality"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only vendors of this criticality. A vendor with none recorded counts as critical.",
              "title": "Criticality"
            },
            "description": "Only vendors of this criticality. A vendor with none recorded counts as critical."
          },
          {
            "name": "lifeCycle",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VendorLifeCycle"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only vendors at this lifecycle stage. 'Vendors we use today' is lifeCycle=active.",
              "title": "Lifecycle"
            },
            "description": "Only vendors at this lifecycle stage. 'Vendors we use today' is lifeCycle=active."
          },
          {
            "name": "businessRisk",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VendorRiskLevel"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only vendors rated at this overall business risk.",
              "title": "Businessrisk"
            },
            "description": "Only vendors rated at this overall business risk."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VendorCategory"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only vendors in this category - the catalog's category for a catalog vendor the company did not recategorise.",
              "title": "Category"
            },
            "description": "Only vendors in this category - the catalog's category for a catalog vendor the company did not recategorise."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "name",
                "createdAt",
                "updatedAt",
                "lastReviewDate",
                "nextReviewDate"
              ],
              "type": "string",
              "description": "Order of the results. Vendors with no value for the field come last when descending; name sorts case-insensitively.",
              "default": "createdAt",
              "title": "Sort"
            },
            "description": "Order of the results. Vendors with no value for the field come last when descending; name sorts case-insensitively."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Newest first (the default); false for oldest first.",
              "default": true,
              "title": "Descending"
            },
            "description": "Newest first (the default); false for oldest first."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Vendor_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/vendors/{id}": {
      "get": {
        "tags": [
          "vendors"
        ],
        "summary": "Get vendor by ID",
        "description": "Return one vendor by id - the same shape as a `get_vendors` row.\n\nUse it to follow up on a specific vendor when you already hold its id; to find vendors\nby name, criticality, lifecycle, risk or category, use `get_vendors`.",
        "operationId": "get_vendor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The vendor's id, as returned by get_vendors.",
              "title": "Id"
            },
            "description": "The vendor's id, as returned by get_vendors."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vendor"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such vendor for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/people": {
      "get": {
        "tags": [
          "people"
        ],
        "summary": "List people",
        "description": "Return people - the employees the company tracks for compliance coverage - one page at a time.\n\nEach row is the whole person: `firstName`, `lastName`, `email`, `jobTitle`,\n`employmentStatus` (active or inactive), `source` (csv or the integration that synced\nthe record), `hiringDate`, `terminationDate`, and the record's `createdAt` /\n`updatedAt`. Where the company edited a synced value in Scytale, the edit is what is\nserved. `get_person` returns the same shape; there is nothing more on the detail.\n\nFilters combine with AND. \"Who works here today\" is `employmentStatus=active`; \"who\nhas left, and when\" is `employmentStatus=inactive`, reading `terminationDate`; \"who\nare our newest joiners\" is `sort=hiringDate` (newest first by default); \"who came from\nour HR system\" is `source=<its internal name>`.\n\nTraining, onboarding and policy-acknowledgement status are not on this resource:\nthey are computed from the person's group memberships and are not part of the People\nschema.\n\n`pagination.total` is every person that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated. People the company excluded from compliance scope are not\nreturned.",
        "operationId": "get_people",
        "parameters": [
          {
            "name": "employmentStatus",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/EmploymentStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only people with this employment status. active is the current workforce; inactive is everyone who has left (there is no 'terminated' status - read `terminationDate`).",
              "title": "Employmentstatus"
            },
            "description": "Only people with this employment status. active is the current workforce; inactive is everyone who has left (there is no 'terminated' status - read `terminationDate`)."
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 64
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only people whose record came from this source: `csv` for a CSV import, otherwise an integration's internal name as it appears on the rows (bamboohr, hibob, personio, deel, okta, microsoft-graph, google-workspace-hr, ...). A source none of this company's people came from is refused with a 400 rather than answered with an empty page.",
              "title": "Source"
            },
            "description": "Only people whose record came from this source: `csv` for a CSV import, otherwise an integration's internal name as it appears on the rows (bamboohr, hibob, personio, deel, okta, microsoft-graph, google-workspace-hr, ...). A source none of this company's people came from is refused with a 400 rather than answered with an empty page."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "createdAt",
                "updatedAt",
                "hiringDate",
                "terminationDate",
                "lastName",
                "email"
              ],
              "type": "string",
              "description": "Order of the results. People with no value for the field come last when descending; lastName and email sort case-insensitively.",
              "default": "createdAt",
              "title": "Sort"
            },
            "description": "Order of the results. People with no value for the field come last when descending; lastName and email sort case-insensitively."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Newest first (the default); false for oldest first.",
              "default": true,
              "title": "Descending"
            },
            "description": "Newest first (the default); false for oldest first."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Person_"
                }
              }
            }
          },
          "400": {
            "description": "A filter value is not valid - including a `source` none of this company's people came from. The error code is `bad_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/people/{id}": {
      "get": {
        "tags": [
          "people"
        ],
        "summary": "Get person by ID",
        "description": "Return one person by id - the same shape as a `get_people` row.\n\nUse it to follow up on a specific person when you already hold their id; to find\npeople by employment status or source, use `get_people`.",
        "operationId": "get_person",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The person's id, as returned by get_people.",
              "title": "Id"
            },
            "description": "The person's id, as returned by get_people."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Person"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such person for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/risks": {
      "get": {
        "tags": [
          "risks"
        ],
        "summary": "List risks",
        "description": "Return the company's risk register - each risk with its scores, treatment and owner -\none page at a time.\n\nEach row carries the risk's title and description, the kind of asset it threatens and\nthat asset's classification, its inherent `likelihood` and `impact` (before treatment)\nand its `residualLikelihood` and `residualImpact` (after), the chosen treatment\n(`treatment`: mitigate, transfer, avoid, accept), how far along it is\n(`treatmentStatus`: incomplete, complete), the treatment plan (`treatmentPlan`: its\ntasks, each with whether it is `completed`, and the rationale written for a transfer,\navoid or accept decision under `rationales.<treatment>`), free-text notes\n(`mitigationInfo`) and the owner's user id.\nThe detail (`get_risk`) is the same shape; nothing is held back from the list.\nFilters combine with AND.\n\nScores are integers on the company's own scale, 1..N where N is its risk matrix size\n(3 to 10, 5 by default); a risk score is likelihood x impact. \"Show our highest-severity\nopen risks\" is `treatmentStatus=incomplete` sorted by likelihood x impact on the\nclient. \"Which risks have we accepted vs. are actively treating\" is `treatment=accept`\nagainst `treatment=mitigate`. \"Which risks are still unmitigated\" is\n`treatmentStatus=incomplete`. \"Which treatment tasks are still open\" is the\n`treatmentPlan.tasks` with `completed` false.\n\n`pagination.total` is every risk that matched, across all pages. A null\n`pagination.nextCursor` means there are no more results - it does not mean the\nresults were truncated.",
        "operationId": "get_risks",
        "parameters": [
          {
            "name": "treatmentStatus",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/RiskTreatmentStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only risks whose treatment is in this state: incomplete (still being treated, or not started) or complete. 'Which risks are still unmitigated' is incomplete.",
              "title": "Treatmentstatus"
            },
            "description": "Only risks whose treatment is in this state: incomplete (still being treated, or not started) or complete. 'Which risks are still unmitigated' is incomplete."
          },
          {
            "name": "treatment",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/RiskTreatment"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only risks treated this way: mitigate (reduced with controls), transfer (passed to a third party), avoid (the activity stopped) or accept (lived with). A risk with no treatment decided yet matches none of them.",
              "title": "Treatment"
            },
            "description": "Only risks treated this way: mitigate (reduced with controls), transfer (passed to a third party), avoid (the activity stopped) or accept (lived with). A risk with no treatment decided yet matches none of them."
          },
          {
            "name": "ownerId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[0-9a-fA-F]{24}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only risks owned by this user, by exact user id (not a name).",
              "title": "Ownerid"
            },
            "description": "Only risks owned by this user, by exact user id (not a name)."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "createdAt",
                "updatedAt",
                "displayName"
              ],
              "type": "string",
              "description": "Order of the results.",
              "default": "createdAt",
              "title": "Sort"
            },
            "description": "Order of the results."
          },
          {
            "name": "descending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Sort descending instead of ascending.",
              "default": true,
              "title": "Descending"
            },
            "description": "Sort descending instead of ascending."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor from a previous page's `pagination.nextCursor`.",
              "title": "Cursor"
            },
            "description": "Cursor from a previous page's `pagination.nextCursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "description": "Results per page (1-100).",
              "default": 50,
              "title": "Limit"
            },
            "description": "Results per page (1-100)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Risk_"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/risks/{id}": {
      "get": {
        "tags": [
          "risks"
        ],
        "summary": "Get risk by ID",
        "description": "Return one risk by id - the same shape as a `get_risks` row.\n\nUse it to follow up on a specific risk when you already hold its id; to find risks by\ntreatment, status or owner, use `get_risks`.",
        "operationId": "get_risk",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$",
              "description": "The risk's id, as returned by get_risks.",
              "title": "Id"
            },
            "description": "The risk's id, as returned by get_risks."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Risk"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unknown parameter, page size or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such risk for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Data is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApprovalManagement": {
        "type": "string",
        "enum": [
          "scytale",
          "external"
        ],
        "title": "ApprovalManagement",
        "description": "Who owns approval of an external policy: Scytale, or the external system it came from.\n\nOnly meaningful on an external policy; null on an internal one. When the external system\nowns approval, Scytale does not track a next sign-off date, so `signOffExpiresAt` is null."
      },
      "Audit": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "framework": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FrameworkRef"
              },
              {
                "type": "null"
              }
            ],
            "description": "The framework being audited."
          },
          "status": {
            "$ref": "#/components/schemas/AuditStatus",
            "description": "Lifecycle status: active, not_active or completed."
          },
          "startDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Start of the audit period; null when not set."
          },
          "endDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the audit period; null when not set."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the audit was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the audit."
          }
        },
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "title": "Audit",
        "description": "One audit as a list row: the framework being audited, where it stands and its period."
      },
      "AuditDetail": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "framework": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FrameworkRef"
              },
              {
                "type": "null"
              }
            ],
            "description": "The framework being audited."
          },
          "status": {
            "$ref": "#/components/schemas/AuditStatus",
            "description": "Lifecycle status: active, not_active or completed."
          },
          "startDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Start of the audit period; null when not set."
          },
          "endDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the audit period; null when not set."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the audit was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the audit."
          },
          "product": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProductRef"
              },
              {
                "type": "null"
              }
            ],
            "description": "The product audited; null when not per-product."
          }
        },
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "title": "AuditDetail",
        "description": "One audit in full: a list row plus the product it is scoped to, when the framework is\ncertified per product."
      },
      "AuditStatus": {
        "type": "string",
        "enum": [
          "active",
          "not_active",
          "completed"
        ],
        "title": "AuditStatus",
        "description": "Where an audit is in its lifecycle. Stored by the product; served as snake_case."
      },
      "Control": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "code": {
            "type": "string",
            "description": "Scytale's control reference, e.g. CC.01.08 - the company's override when set."
          },
          "name": {
            "type": "string",
            "description": "Human-readable control name."
          },
          "description": {
            "type": "string",
            "description": "What the control requires."
          },
          "applicable": {
            "type": "boolean",
            "description": "False when scoped out of this company's programme. Lists and counts here include inapplicable controls unless filtered by `applicable`; the product's dashboard counts applicable controls only, so totals differ by the inapplicable ones."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns the control; null when unassigned."
          },
          "criteria": {
            "items": {
              "$ref": "#/components/schemas/Criterion"
            },
            "type": "array",
            "description": "Framework criteria the control maps to."
          },
          "auditId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The audit this control sits under."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the control was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the control."
          },
          "monitorIds": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Monitors feeding this control."
          },
          "framework": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FrameworkRef"
              },
              {
                "type": "null"
              }
            ],
            "description": "The framework it derives from."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "description",
          "applicable"
        ],
        "title": "Control",
        "description": "One control: the twelve public fields, identical on the list and the detail route.\n\nTwo more are read on every control and never served - `exclude=True` keeps them out of\nthe JSON and out of the OpenAPI schema: `rank`, the product's display order, used for\nsorting and the keyset cursor; and `status`, derived from the linked monitorings\n(`data/control_state.py`), which drives `find_open_audit_items` and its summary but is\n**not part of the public contract**."
      },
      "ControlRef": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Control id, as `get_control` takes it."
          },
          "code": {
            "type": "string",
            "description": "Scytale's control reference, e.g. CC.01.08 - the company's override when set."
          },
          "name": {
            "type": "string",
            "description": "Human-readable control name."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "name"
        ],
        "title": "ControlRef",
        "description": "A control this monitor feeds, named so a failing check can be traced to what it\naffects without a second call."
      },
      "Criterion": {
        "properties": {
          "code": {
            "type": "string",
            "description": "The framework's own reference for the criterion."
          },
          "description": {
            "type": "string",
            "description": "What the criterion requires."
          }
        },
        "type": "object",
        "required": [
          "code",
          "description"
        ],
        "title": "Criterion",
        "description": "A framework criterion the control maps to, e.g. SOC 2 CC3.3 or ISO 27001 A.9.4."
      },
      "EmploymentStatus": {
        "type": "string",
        "enum": [
          "active",
          "inactive"
        ],
        "title": "EmploymentStatus",
        "description": "Whether the person currently works for the company. The product's `EmployeeStatusTypes`:\ntwo values, no \"terminated\" - someone who left is `inactive` with a `terminationDate`."
      },
      "ErrorDetail": {
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "title": "ErrorDetail"
      },
      "ErrorResponse": {
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "type": "object",
        "required": [
          "error"
        ],
        "title": "ErrorResponse",
        "description": "The body of every non-2xx answer. Also the schema the OpenAPI document names."
      },
      "FrameworkRef": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Framework id."
          },
          "name": {
            "type": "string",
            "description": "Framework display name, e.g. 'SOC 2 Type II'."
          }
        },
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "title": "FrameworkRef",
        "description": "A compliance framework, e.g. SOC 2 or ISO 27001."
      },
      "Monitor": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "code": {
            "type": "string",
            "description": "Scytale's monitor reference, e.g. SRC.04 or MON.12 for a custom monitor."
          },
          "name": {
            "type": "string",
            "description": "Human-readable monitor name."
          },
          "description": {
            "type": "string",
            "description": "What the monitor checks; empty when the product has none."
          },
          "state": {
            "$ref": "#/components/schemas/MonitorState",
            "description": "Verdict of the latest automated evidence: non-compliant (a check failed since the last review), compliant (checks ran and none is failing) or pending (no automated evidence collected - manual monitors, or an integration that has not run)."
          },
          "frequency": {
            "$ref": "#/components/schemas/MonitorFrequency",
            "description": "How often the check is meant to run."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns the monitor; null when unassigned."
          },
          "inactive": {
            "type": "boolean",
            "description": "True when the monitor is switched off - by a user, or automatically because no active control needs it. An inactive monitor still lists, so a caller can see it."
          },
          "isMandatory": {
            "type": "boolean",
            "description": "True when the product requires this monitor for its frameworks."
          },
          "isCustom": {
            "type": "boolean",
            "description": "True when the company created this monitor itself rather than from a template."
          },
          "dueDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When manual evidence is next due, if a due date is active; null otherwise."
          },
          "integrationNames": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Integrations that feed this monitor, by identifier (e.g. aws-iam, github). Empty for a manual monitor."
          },
          "problemDescription": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What a failing result means, in the product's words; null when it has none."
          },
          "howToFixUrl": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the product points for remediation guidance."
          },
          "lastRunAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When automated evidence for this monitor was last collected or updated; null if it never has been."
          },
          "controlCount": {
            "type": "integer",
            "description": "How many controls in this company this monitor feeds. 0 when unlinked. The controls themselves are on `get_monitor`."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the monitor was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the monitor."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "description",
          "state",
          "frequency",
          "inactive",
          "isMandatory",
          "isCustom",
          "controlCount"
        ],
        "title": "Monitor",
        "description": "One monitor: what it checks, how it is fed, whether it is passing, and what it affects.\n\nThe per-evidence verdicts the state is derived from (`evidencesCompliant`, one entry per\ncollected evidence item) are read on every monitor and never served: they are large,\ninternal, and summarised by `state` and `lastRunAt`."
      },
      "MonitorDetail": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "code": {
            "type": "string",
            "description": "Scytale's monitor reference, e.g. SRC.04 or MON.12 for a custom monitor."
          },
          "name": {
            "type": "string",
            "description": "Human-readable monitor name."
          },
          "description": {
            "type": "string",
            "description": "What the monitor checks; empty when the product has none."
          },
          "state": {
            "$ref": "#/components/schemas/MonitorState",
            "description": "Verdict of the latest automated evidence: non-compliant (a check failed since the last review), compliant (checks ran and none is failing) or pending (no automated evidence collected - manual monitors, or an integration that has not run)."
          },
          "frequency": {
            "$ref": "#/components/schemas/MonitorFrequency",
            "description": "How often the check is meant to run."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns the monitor; null when unassigned."
          },
          "inactive": {
            "type": "boolean",
            "description": "True when the monitor is switched off - by a user, or automatically because no active control needs it. An inactive monitor still lists, so a caller can see it."
          },
          "isMandatory": {
            "type": "boolean",
            "description": "True when the product requires this monitor for its frameworks."
          },
          "isCustom": {
            "type": "boolean",
            "description": "True when the company created this monitor itself rather than from a template."
          },
          "dueDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When manual evidence is next due, if a due date is active; null otherwise."
          },
          "integrationNames": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Integrations that feed this monitor, by identifier (e.g. aws-iam, github). Empty for a manual monitor."
          },
          "problemDescription": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What a failing result means, in the product's words; null when it has none."
          },
          "howToFixUrl": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the product points for remediation guidance."
          },
          "lastRunAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When automated evidence for this monitor was last collected or updated; null if it never has been."
          },
          "controlCount": {
            "type": "integer",
            "description": "How many controls in this company this monitor feeds. 0 when unlinked. The controls themselves are on `get_monitor`."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the monitor was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the monitor."
          },
          "controls": {
            "items": {
              "$ref": "#/components/schemas/ControlRef"
            },
            "type": "array",
            "description": "The controls this monitor feeds, in this company, ordered by code. Empty when unlinked. Fetch this for the rows you are asking about rather than paging the list for it - a monitor commonly feeds the same requirement across several frameworks."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "description",
          "state",
          "frequency",
          "inactive",
          "isMandatory",
          "isCustom",
          "controlCount"
        ],
        "title": "MonitorDetail",
        "description": "One monitor in full: a list row plus the controls it feeds."
      },
      "MonitorFrequency": {
        "type": "string",
        "enum": [
          "manual",
          "daily",
          "weekly",
          "monthly",
          "quarterly",
          "semi-annual",
          "annual"
        ],
        "title": "MonitorFrequency",
        "description": "How often the check is meant to run. The product's `MonitoringFrequencies`; an\nautomated monitor is `daily`, a manual one defaults to `annual`."
      },
      "MonitorState": {
        "type": "string",
        "enum": [
          "compliant",
          "non-compliant",
          "pending"
        ],
        "title": "MonitorState",
        "description": "The verdict of the monitor's latest automated evidence.\n\n* `non-compliant` - at least one automated check failed, and the failure is newer than\n  the monitor's last review (\"marked as reviewed\" in the product). This is the Control\n  Center's *attention* rule for automated monitors, ported as is.\n* `compliant` - automated evidence has been collected and none of it is failing, or every\n  failure has since been reviewed.\n* `pending` - no automated evidence has been collected yet, so there is no verdict: a\n  manual monitor, or an integration that has not run.\n\nThree values on purpose. The tickets say \"pass/fail\", but collapsing `pending` into either\nside would report a manual monitor as failing, or a never-run integration as passing.\n`tests/test_rest_contract.py` pins the enum to exactly these three."
      },
      "Owner": {
        "properties": {
          "id": {
            "type": "string",
            "description": "User id."
          },
          "name": {
            "type": "string",
            "description": "Display name; the email when the user has no name on record."
          },
          "email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Email address, when known."
          }
        },
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "title": "Owner",
        "description": "The user who owns a control."
      },
      "PageInfo": {
        "properties": {
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows. False means you have seen everything."
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass as `cursor` to fetch the next page. Null means there are no more results - it does not mean the results were truncated."
          },
          "limit": {
            "type": "integer",
            "description": "The page size actually applied, after clamping to the allowed range."
          },
          "total": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Total across all pages when known. Null means not counted, which is not the same as zero."
          }
        },
        "type": "object",
        "required": [
          "hasMore",
          "limit"
        ],
        "title": "PageInfo",
        "description": "Where a page sits in its collection."
      },
      "Page_Audit_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Audit"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Audit]"
      },
      "Page_Control_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Control"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Control]"
      },
      "Page_Monitor_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Monitor"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Monitor]"
      },
      "Page_Person_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Person"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Person]"
      },
      "Page_Policy_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Policy"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Policy]"
      },
      "Page_Risk_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Risk"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Risk]"
      },
      "Page_Vendor_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Vendor"
            },
            "type": "array",
            "description": "This page of results."
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo",
            "description": "How to fetch the next page, and how much there is."
          }
        },
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[Vendor]"
      },
      "Person": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "firstName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Given name; null when not recorded."
          },
          "lastName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Family name; null when not recorded."
          },
          "email": {
            "type": "string",
            "description": "Work email address, lower-cased. Unique within the company for a given source."
          },
          "jobTitle": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Job title as recorded; null when not recorded."
          },
          "employmentStatus": {
            "$ref": "#/components/schemas/EmploymentStatus",
            "description": "active or inactive. Filterable. There is no 'terminated' status: someone who has left is inactive, and `terminationDate` says when."
          },
          "source": {
            "type": "string",
            "description": "Where the record came from. Filterable. `csv` for a CSV import (the manual path), otherwise the internal name of the HR or identity integration that synced it - for example bamboohr, hibob, personio, deel, okta, microsoft-graph, google-workspace-hr."
          },
          "hiringDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the person joined; null when the source did not provide one."
          },
          "terminationDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the person left; null while they are employed or when the source did not provide one."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created in Scytale."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Last change to the record in Scytale, including integration syncs."
          }
        },
        "type": "object",
        "required": [
          "id",
          "email",
          "employmentStatus",
          "source",
          "createdAt",
          "updatedAt"
        ],
        "title": "Person",
        "description": "One person: who they are, whether they still work here, where the record came from, and\nwhen they joined and left.\n\nEvery value is the effective one: a manual edit made in Scytale wins over the value the\nintegration last synced, on every read, so the list row and the detail never disagree with\nwhat the product shows."
      },
      "Policy": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "code": {
            "type": "string",
            "description": "Scytale's policy reference, e.g. POL.01."
          },
          "title": {
            "type": "string",
            "description": "The policy's title."
          },
          "state": {
            "$ref": "#/components/schemas/PolicyState",
            "description": "Compliance status: signed-off, approval-required (never signed, edited since signing, or the sign-off expired) or review-required."
          },
          "workflowStatus": {
            "$ref": "#/components/schemas/PolicyWorkflowStatus",
            "description": "Where the policy sits in its review and approval cycle."
          },
          "version": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PolicyVersion"
              },
              {
                "type": "null"
              }
            ],
            "description": "Latest version number; null when the policy has no version yet."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns the policy; null when unassigned."
          },
          "approver": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who must approve this cycle; null when no approver is assigned."
          },
          "signedOffAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the policy was last signed off; null if it never has been."
          },
          "signedOffBy": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who signed it off; null if it never has been."
          },
          "signOffExpiresAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the current sign-off lapses, one year after it was given. Null when the policy is unsigned, or when an external system owns its approval."
          },
          "confidentiality": {
            "$ref": "#/components/schemas/PolicyConfidentiality"
          },
          "isExternalPolicy": {
            "type": "boolean",
            "description": "True when the policy is managed outside Scytale."
          },
          "approvalManagement": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ApprovalManagement"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns approval of an external policy; null on an internal one."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the policy was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the policy."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "title",
          "state",
          "workflowStatus",
          "confidentiality",
          "isExternalPolicy"
        ],
        "title": "Policy",
        "description": "One policy as a list row: its identity, who owns it, and where it stands.\n\nMetadata only - the policy document itself is never served (`html` is excluded, and the\nsigned PDF is not reachable through this API)."
      },
      "PolicyConfidentiality": {
        "type": "string",
        "enum": [
          "restricted",
          "confidential",
          "internal-use",
          "public"
        ],
        "title": "PolicyConfidentiality",
        "description": "How widely the policy may be shared."
      },
      "PolicyDetail": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "code": {
            "type": "string",
            "description": "Scytale's policy reference, e.g. POL.01."
          },
          "title": {
            "type": "string",
            "description": "The policy's title."
          },
          "state": {
            "$ref": "#/components/schemas/PolicyState",
            "description": "Compliance status: signed-off, approval-required (never signed, edited since signing, or the sign-off expired) or review-required."
          },
          "workflowStatus": {
            "$ref": "#/components/schemas/PolicyWorkflowStatus",
            "description": "Where the policy sits in its review and approval cycle."
          },
          "version": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PolicyVersion"
              },
              {
                "type": "null"
              }
            ],
            "description": "Latest version number; null when the policy has no version yet."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns the policy; null when unassigned."
          },
          "approver": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who must approve this cycle; null when no approver is assigned."
          },
          "signedOffAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the policy was last signed off; null if it never has been."
          },
          "signedOffBy": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Owner"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who signed it off; null if it never has been."
          },
          "signOffExpiresAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the current sign-off lapses, one year after it was given. Null when the policy is unsigned, or when an external system owns its approval."
          },
          "confidentiality": {
            "$ref": "#/components/schemas/PolicyConfidentiality"
          },
          "isExternalPolicy": {
            "type": "boolean",
            "description": "True when the policy is managed outside Scytale."
          },
          "approvalManagement": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ApprovalManagement"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who owns approval of an external policy; null on an internal one."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the policy was created."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the policy."
          },
          "reviewers": {
            "items": {
              "$ref": "#/components/schemas/Reviewer"
            },
            "type": "array",
            "description": "Reviewers for this cycle and whether each has completed; empty when none are assigned."
          }
        },
        "type": "object",
        "required": [
          "id",
          "code",
          "title",
          "state",
          "workflowStatus",
          "confidentiality",
          "isExternalPolicy"
        ],
        "title": "PolicyDetail",
        "description": "One policy in full: a list row plus this cycle's reviewers."
      },
      "PolicyState": {
        "type": "string",
        "enum": [
          "signed-off",
          "approval-required",
          "review-required"
        ],
        "title": "PolicyState",
        "description": "Whether the policy is signed off, and whether that sign-off still stands.\n\nThe product's `PolicyState`. A sign-off is valid for one year; an expired one returns\nthe policy to `approval-required` rather than leaving it `signed-off`."
      },
      "PolicyVersion": {
        "properties": {
          "major": {
            "type": "integer",
            "description": "Major version; 0 until the policy is first signed off."
          },
          "minor": {
            "type": "integer",
            "description": "Minor version, incremented on each edit."
          }
        },
        "type": "object",
        "required": [
          "major",
          "minor"
        ],
        "title": "PolicyVersion",
        "description": "The policy's latest version number.\n\nA policy starts at 0.1 and reaches 1.0 on its first sign-off, so anything past 0.1 has\nbeen edited since - which is what drives `state` and `workflowStatus` for a policy that\nhas never been signed."
      },
      "PolicyWorkflowStatus": {
        "type": "string",
        "enum": [
          "pending",
          "in-progress",
          "in-review",
          "pending-approval",
          "signed-off"
        ],
        "title": "PolicyWorkflowStatus",
        "description": "Where the policy sits in its review and approval cycle.\n\nThe product's `PolicyWorkflowStatus`. `pending-approval` is reachable only when an\napprover is assigned; without one, completed reviews leave sign-off open to anyone and\nthe policy stays `in-review`."
      },
      "ProductRef": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Product id."
          },
          "name": {
            "type": "string",
            "description": "Product display name."
          }
        },
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "title": "ProductRef",
        "description": "A product of the company, when a framework is certified per product."
      },
      "Reviewer": {
        "properties": {
          "user": {
            "$ref": "#/components/schemas/Owner",
            "description": "The reviewer."
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When they completed their review; null while still outstanding."
          }
        },
        "type": "object",
        "required": [
          "user"
        ],
        "title": "Reviewer",
        "description": "Someone asked to review the policy this cycle, and whether they have."
      },
      "Risk": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "displayName": {
            "type": "string",
            "description": "The risk's title as it appears in the register."
          },
          "description": {
            "type": "string",
            "description": "What the risk is; may be empty."
          },
          "asset": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RiskAsset"
              },
              {
                "type": "null"
              }
            ],
            "description": "The kind of asset at risk: information-data-assets, ict-asset-infrastructure, people, operational-ict-processes, third-party-ict-service-providers or financial-digital-assets; older risks may carry a legacy value (data, devices, financial, information, policy, process, repository, vendor). Null when not recorded."
          },
          "assetClassification": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RiskClassification"
              },
              {
                "type": "null"
              }
            ],
            "description": "Sensitivity of the asset at risk: internal, confidential, restricted or public. Null when not recorded."
          },
          "likelihood": {
            "type": "integer",
            "description": "Inherent likelihood, before treatment, on the company's scale (1..N, N = matrix size, 3-10, default 5)."
          },
          "impact": {
            "type": "integer",
            "description": "Inherent impact, before treatment, on the same scale."
          },
          "residualLikelihood": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Likelihood after treatment, on the same scale; null when not assessed."
          },
          "residualImpact": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Impact after treatment, on the same scale; null when not assessed."
          },
          "treatment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RiskTreatment"
              },
              {
                "type": "null"
              }
            ],
            "description": "The chosen treatment: mitigate, transfer, avoid or accept. Filterable. Null when the company has not decided yet."
          },
          "treatmentStatus": {
            "$ref": "#/components/schemas/RiskTreatmentStatus",
            "description": "incomplete or complete. Filterable. For a mitigated risk the product derives it from the linked controls; it may also be set by hand. 'Which risks are still unmitigated' is treatmentStatus=incomplete."
          },
          "treatmentPlan": {
            "$ref": "#/components/schemas/TreatmentPlan",
            "description": "The treatment plan: its tasks with their completion state, and the rationale written for a transfer, avoid or accept decision."
          },
          "mitigationInfo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Free-text mitigation notes the company wrote on the risk (the plan as text, from before plans had tasks); null when none."
          },
          "ownerId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the user in this company who owns the risk; the first owner when several are assigned. Filterable. Null when unassigned."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the risk was added to the register."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the risk."
          }
        },
        "type": "object",
        "required": [
          "id",
          "displayName",
          "description",
          "likelihood",
          "impact",
          "treatmentStatus"
        ],
        "title": "Risk",
        "description": "One risk from the register: what it is, what it threatens, how it scores before and\nafter treatment, how it is treated and how far that has got, and who owns it.\n\nEvery value is the stored one. Risks have no override mechanism: a risk created from a\ntemplate copies the template's values once and the document is the single source of\ntruth from then on."
      },
      "RiskAsset": {
        "type": "string",
        "enum": [
          "information-data-assets",
          "ict-asset-infrastructure",
          "people",
          "operational-ict-processes",
          "third-party-ict-service-providers",
          "financial-digital-assets",
          "data",
          "devices",
          "financial",
          "information",
          "policy",
          "process",
          "repository",
          "vendor"
        ],
        "title": "RiskAsset",
        "description": "The kind of asset the risk threatens. The product's current six values, plus the\nlegacy set older risks still carry (`people` is in both). Stored values outside the\nunion are served as null."
      },
      "RiskClassification": {
        "type": "string",
        "enum": [
          "internal",
          "confidential",
          "restricted",
          "public"
        ],
        "title": "RiskClassification",
        "description": "The sensitivity class of the asset at risk."
      },
      "RiskTreatment": {
        "type": "string",
        "enum": [
          "mitigate",
          "transfer",
          "avoid",
          "accept"
        ],
        "title": "RiskTreatment",
        "description": "How the company chose to treat the risk. The product's `RiskTreatmentTypes`: reduce it\nwith controls (mitigate), pass it to a third party (transfer), stop the activity that\ncarries it (avoid), or live with it (accept)."
      },
      "RiskTreatmentStatus": {
        "type": "string",
        "enum": [
          "incomplete",
          "complete"
        ],
        "title": "RiskTreatmentStatus",
        "description": "How far along the treatment is. Two values, not a scale: for a mitigated risk the\nproduct derives it from the linked controls (complete when every control in an active\naudit is compliant), and a user may also set it directly."
      },
      "TreatmentPlan": {
        "properties": {
          "tasks": {
            "items": {
              "$ref": "#/components/schemas/TreatmentTask"
            },
            "type": "array",
            "description": "The plan's tasks, in plan order. Empty when there are none."
          },
          "rationales": {
            "$ref": "#/components/schemas/TreatmentRationales",
            "description": "The justification written for each non-mitigate strategy."
          }
        },
        "type": "object",
        "title": "TreatmentPlan",
        "description": "How the chosen treatment is carried out: the tasks (for mitigate) and the rationales\n(for transfer, avoid and accept). Always present; a risk with no plan has no tasks and\nno rationales."
      },
      "TreatmentRationales": {
        "properties": {
          "accept": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the risk is accepted; null when none."
          },
          "avoid": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "How and why the risk is avoided; null when none."
          },
          "transfer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "To whom and how the risk is transferred; null when none."
          }
        },
        "type": "object",
        "title": "TreatmentRationales",
        "description": "The written justification for each non-mitigate strategy, keyed by the `treatment`\nvalue it justifies - so `treatmentPlan.rationales[treatment]` is the current one. A\nmitigated risk documents tasks instead of a rationale. A rationale written for a strategy\nthe risk no longer uses is kept, as the product keeps it."
      },
      "TreatmentTask": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier of the task within its risk."
          },
          "title": {
            "type": "string",
            "description": "The task's title; may be empty."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the task involves; null when none. Often the substance of the task, with a placeholder title such as 'Mitigation plan'."
          },
          "completed": {
            "type": "boolean",
            "description": "Whether the task is done. 'Which tasks are still open' is completed=false."
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the task was marked done; null when open."
          },
          "completedBy": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the user who marked the task done; null when open or not recorded."
          },
          "source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TreatmentTaskSource"
              },
              {
                "type": "null"
              }
            ],
            "description": "auto (copied from the risk template when the risk was created) or manual (added by a user). Null for a stored value outside those two."
          }
        },
        "type": "object",
        "required": [
          "id",
          "title",
          "completed"
        ],
        "title": "TreatmentTask",
        "description": "One step of the treatment plan, with whether it is done."
      },
      "TreatmentTaskSource": {
        "type": "string",
        "enum": [
          "auto",
          "manual"
        ],
        "title": "TreatmentTaskSource",
        "description": "Where a treatment task came from: copied from the risk template when the risk was\ncreated (auto), or added by a user (manual)."
      },
      "Vendor": {
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier."
          },
          "name": {
            "type": "string",
            "description": "Vendor name as the company knows it; for a catalog vendor, the catalog name unless renamed."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the vendor provides; null when neither the company nor the catalog has one."
          },
          "website": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The vendor's website; null when unknown."
          },
          "category": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorCategory"
              },
              {
                "type": "null"
              }
            ],
            "description": "What kind of service the vendor provides. Filterable. For a catalog vendor this is the catalog's category unless the company set its own; null when neither has one."
          },
          "criticality": {
            "$ref": "#/components/schemas/VendorCriticality",
            "description": "critical (the default) or non-critical: whether the business depends on this vendor."
          },
          "lifeCycle": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorLifeCycle"
              },
              {
                "type": "null"
              }
            ],
            "description": "planned, poc, active or terminated. 'Which vendors do we currently use' is lifeCycle=active. Null when the stored value is not one of these."
          },
          "businessRisk": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorRiskLevel"
              },
              {
                "type": "null"
              }
            ],
            "description": "The company's overall risk rating of this vendor: high, medium or low. Null when the stored value is not one of these."
          },
          "confidentialityRisk": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorRiskLevel"
              },
              {
                "type": "null"
              }
            ],
            "description": "Risk to the confidentiality of the data the vendor handles; null when not assessed."
          },
          "integrityRisk": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorRiskLevel"
              },
              {
                "type": "null"
              }
            ],
            "description": "Risk to the integrity of the data the vendor handles; null when not assessed."
          },
          "availabilityRisk": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorRiskLevel"
              },
              {
                "type": "null"
              }
            ],
            "description": "Risk to the availability of the vendor's service; null when not assessed."
          },
          "dataClassification": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorDataClassification"
              },
              {
                "type": "null"
              }
            ],
            "description": "Most sensitive class of data the vendor handles; null when not recorded."
          },
          "sensitiveDataTypes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Kinds of sensitive data the vendor handles. Documented values: personal-identifiable, protected-health, customer-data, business-data, financial-data, employee-data, marketing-data, payment, other - other values may appear. Empty when none recorded."
          },
          "certifications": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Security certifications on record, by framework identifier (e.g. soc2, iso27001). Null when never collected; empty when collected and none were found."
          },
          "dpaOnFile": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether a data processing agreement is on file; null when unknown."
          },
          "signedContract": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether a signed contract is on file; null when unknown."
          },
          "penetrationTestConducted": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether the vendor has had a penetration test; null when unknown."
          },
          "termsOfUseLink": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Link to the vendor's terms of use; null when unknown."
          },
          "primaryContactName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the company's primary contact at the vendor; null when unknown."
          },
          "ownerId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the user in this company who owns the vendor relationship; null when unassigned."
          },
          "reviewFrequency": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VendorReviewFrequency"
              },
              {
                "type": "null"
              }
            ],
            "description": "How often the vendor is reviewed: quarterly, semi-annual, annual, or not-set when the company explicitly chose none. Null when no frequency has been recorded."
          },
          "lastReviewDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the vendor was last reviewed; null if it never has been."
          },
          "nextReviewDate": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the next review is due; null when no review is scheduled."
          },
          "source": {
            "$ref": "#/components/schemas/VendorSource",
            "description": "common: added from Scytale's vendor catalog; custom: created by the company."
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the vendor was added."
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Last change to the vendor."
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "criticality",
          "source"
        ],
        "title": "Vendor",
        "description": "One vendor: identity, how critical and how risky, what data it handles, what due\ndiligence is on file, and where its review cycle stands.\n\nEvery value is the effective one - what the company recorded, or for a catalog vendor\nthe catalog's value where the company recorded nothing - so the list row and the detail\nnever disagree."
      },
      "VendorCategory": {
        "type": "string",
        "enum": [
          "security",
          "identity-and-access-management",
          "legal-and-compliance",
          "finance-and-payments",
          "hr-and-people",
          "developer-tools",
          "monitoring-and-observability",
          "it-operations",
          "design",
          "analytics-and-data",
          "ai-and-machine-learning",
          "communication-and-telecom",
          "customer-support",
          "crm-and-sales",
          "marketing-and-advertising",
          "project-and-product-management",
          "collaboration-and-productivity",
          "ecommerce-and-marketplace",
          "cloud-and-infrastructure",
          "business-operations"
        ],
        "title": "VendorCategory",
        "description": "What kind of service the vendor provides. The product's `VendorCategories`."
      },
      "VendorCriticality": {
        "type": "string",
        "enum": [
          "critical",
          "non-critical"
        ],
        "title": "VendorCriticality",
        "description": "How much the business depends on the vendor. Two values, not a scale: the product asks\n\"could we operate without them\", not \"how much would it hurt\"."
      },
      "VendorDataClassification": {
        "type": "string",
        "enum": [
          "public",
          "internal-use",
          "confidential",
          "restricted"
        ],
        "title": "VendorDataClassification",
        "description": "The most sensitive class of the company's data the vendor handles."
      },
      "VendorLifeCycle": {
        "type": "string",
        "enum": [
          "planned",
          "poc",
          "active",
          "terminated"
        ],
        "title": "VendorLifeCycle",
        "description": "Where the relationship stands. `active` is \"we use them today\"; `planned` and `poc`\nprecede it, `terminated` follows it."
      },
      "VendorReviewFrequency": {
        "type": "string",
        "enum": [
          "quarterly",
          "semi-annual",
          "annual",
          "not-set"
        ],
        "title": "VendorReviewFrequency",
        "description": "How often the company re-reviews the vendor. `not-set` is a stored choice, distinct\nfrom a vendor that has no frequency recorded at all (served as null)."
      },
      "VendorRiskLevel": {
        "type": "string",
        "enum": [
          "high",
          "medium",
          "low"
        ],
        "title": "VendorRiskLevel",
        "description": "A three-step risk rating, used for the overall business risk and for each of the\nconfidentiality, integrity and availability risks."
      },
      "VendorSource": {
        "type": "string",
        "enum": [
          "common",
          "custom"
        ],
        "title": "VendorSource",
        "description": "Where the vendor record came from: Scytale's shared vendor catalog, or created by the\ncompany from scratch."
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Access token from `POST /oauth/token`. Scopes are enforced per route."
      }
    }
  },
  "tags": [
    {
      "name": "controls",
      "x-displayName": "Controls",
      "description": "Controls are the core compliance unit in Scytale: each represents a security\nrequirement mapped to one or more framework criteria (e.g., SOC 2 CC6.1,\nISO 27001 A.9.4). Use this endpoint to retrieve your control set with owner,\napplicability, framework mapping, and linked monitors. Filter by audit,\napplicability, or owner to build control-status reports and ownership views.\n\nRelated resources: Audits, Monitors.\n"
    },
    {
      "name": "audits",
      "x-displayName": "Audits",
      "description": "An audit represents your company's engagement toward a specific framework\ncertification (e.g., a SOC 2 or ISO 27001 audit), including its status and\nperiod. Use this endpoint to retrieve your audits and track readiness across\nframeworks. Filter by status or framework.\n\nRelated resources: Controls.\n"
    },
    {
      "name": "policies",
      "x-displayName": "Policies",
      "description": "Policies are the security and compliance policies managed in Scytale, each with\nan owner, version, and sign-off status. Use this endpoint to retrieve your\npolicies and track coverage and outstanding approvals. Filter by owner, state,\nor workflow status.\n\nRelated resources: Controls.\n"
    },
    {
      "name": "monitors",
      "x-displayName": "Monitors",
      "description": "Monitors are the automated and manual checks that continuously verify a\ncontrol's requirements are being met, each linked to one or more controls and\noften fed by integrations. Use this endpoint to retrieve monitor state\n(compliant / non-compliant / pending), frequency, last run, and connected\nintegrations. Filter by state, owner, frequency, or active/inactive.\n\nRelated resources: Controls.\n"
    },
    {
      "name": "vendors",
      "x-displayName": "Vendors",
      "description": "Vendors are the third parties you share information with, each assessed for\nthe risk that relationship carries. Use this endpoint to retrieve your vendor\nregister with criticality, lifecycle stage, data classification, business and\nsecurity risk ratings, certifications held, and review dates. Filter by\ncriticality, lifecycle, business risk, or category to surface the vendors\nthat need attention or to drive a review cycle.\n"
    },
    {
      "name": "people",
      "x-displayName": "People",
      "description": "People are the team members in scope for your compliance program, the\npopulation your controls and policies apply to. Use this endpoint to\nretrieve your roster with name, work email, job title, employment status,\nand how each person was added, whether manually or through a connected HR\nintegration. Filter by employment status or source to scope a report to\ncurrent employees, or to the people a particular system brought in.\n\nRelated resources: Policies, Controls.\n"
    },
    {
      "name": "risks",
      "x-displayName": "Risks",
      "description": "Risks are the entries in your risk register, each scored for likelihood and\nimpact and tracked through to mitigation. Use this endpoint to retrieve your\nregister with the affected asset and its classification, inherent and\nresidual scores, owner, and mitigation plan and status. Filter by mitigation\nstatus, mitigation plan, or owner to report on open exposure or on the risks\na particular team is carrying.\n\nRelated resources: Controls, Vendors.\n"
    }
  ],
  "servers": [
    {
      "url": "https://api.scytale.ai",
      "description": "US - for companies whose Scytale data is hosted in the United States."
    },
    {
      "url": "https://api.eu.scytale.ai",
      "description": "EU - for companies whose Scytale data is hosted in the European Union."
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ]
}