{
  "openapi": "3.1.0",
  "info": {
    "title": "Philter API",
    "version": "4.0.0"
  },
  "servers": [
    {
      "description": "Philter API",
      "url": "/"
    }
  ],
  "paths": {
    "/api/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List the calling key's user's API keys.",
        "description": "Lists the active keys belonging to the calling key's user, oldest first, with the total. Returns each key's ID, prefix, scopes, creation time, and whether it is the bootstrap key, never the key itself. session=true lists only session keys and session=false only long-lived keys; total counts only the keys listed. Requires the `api-keys:read` scope.",
        "operationId": "getApiKeys",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          },
          {
            "description": "true lists only session keys, false only long-lived keys; left out, both. total counts only the keys listed.",
            "name": "session",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetApiKeysResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Create an API key for the calling key's user.",
        "description": "Mints a key for the calling key's user, so a key can be rotated without an administrator. The scopes must be a subset of those the calling key holds. The key is returned once: only its hash is stored. Recorded as an api_key_created audit event naming the calling user and API key. Requires the `api-keys:write` scope.",
        "operationId": "createOwnApiKey",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key was created. The value is returned here and nowhere else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKeyResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "No scopes were given, or one of them is not a scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write, is a session key, or a requested scope is not held by the calling key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/api-keys/current": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Sign out: revoke the calling session key.",
        "description": "Revokes the session key making the request, so a person can sign out without an administrator. Any key may call it, whatever its scopes, because it can only end the caller's own access. A long-lived key cannot revoke itself; revoke it with another key. Recorded as an api_key_deleted audit event with the reason signed out.",
        "operationId": "signOut",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The session key was revoked.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The calling key is a long-lived key, not a session key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/api-keys/scopes": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List the API key scopes.",
        "description": "Returns every scope an API key can carry, with what it allows, in the order Philter declares them, so a client offering scopes to choose from need not hard-code them. Any key may call it, whatever its scopes, since it describes the API rather than any account.",
        "operationId": "getScopes",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyScopesResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/api-keys/{keyId}": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Revoke an API key.",
        "description": "Revokes a key: it stops authenticating at once and cannot be restored. The key making the request cannot revoke itself; revoke it with another key. The calling key cannot revoke a key holding a scope it does not hold. A caller can revoke its own user's keys; an administrator can revoke any user's. Recorded as an api_key_deleted audit event naming the calling user and API key. Requires the `api-keys:write` scope.",
        "operationId": "revokeApiKey",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "keyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The key was revoked.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write, or the key being revoked holds a scope the calling key does not.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no active key with that ID that the caller may manage.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The key is the one making the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/api-keys/{keyId}/scopes": {
      "put": {
        "tags": [
          "API Keys"
        ],
        "summary": "Replace an API key's scopes.",
        "description": "Replaces the scopes on a key. The key value does not change. The new scopes must be a subset of those the calling key holds, and the calling key cannot change a key holding a scope it does not hold. The key making the request cannot change its own scopes. A caller can change its own user's keys; an administrator can change any user's. Recorded as an api_key_scopes_changed audit event with the scopes before and after and the calling user and API key. Requires the `api-keys:write` scope.",
        "operationId": "setApiKeyScopes",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "keyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetApiKeyScopesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The scopes were replaced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "No scopes were given, or one of them is not a scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write, a requested scope is not held by the calling key, the key being changed holds a scope the calling key does not, or the calling key is a session key and the change adds a scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no active key with that ID that the caller may manage.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The key is the one making the request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/audit": {
      "get": {
        "tags": [
          "Audit Log"
        ],
        "summary": "List audit events.",
        "description": "Returns audit events, most recent first, paginated. Filter by event type with event, by time with from and to (ISO-8601 instants; from is inclusive, to is exclusive), and by acting principal with owner. Reading the audit log is itself audited. The log covers the whole deployment, so this requires an administrator as well as the audit:read scope. Requires the `audit:read` scope.",
        "operationId": "getAuditLog",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetAuditResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "A timestamp could not be parsed, the range is reversed, or the event type is not one Philter emits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold audit:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/audit/export": {
      "get": {
        "tags": [
          "Audit Log"
        ],
        "summary": "Export the audit log as CSV.",
        "description": "Returns the audit log for a range of whole days as CSV, most recent first. from and to are dates (YYYY-MM-DD), both inclusive, read in zone (an IANA time zone such as UTC or America/New_York), or in the server's time zone when zone is omitted. to may be at most 30 days after from. Returns up to limit events (default 100, at most 1000; a larger value is treated as the maximum, and zero or less as the default). The X-Philter-Export-Truncated header is true when more remain, and X-Philter-Export-Next-Cursor then gives the cursor to request the next page with. Events are ordered by timestamp and then id, and a cursor continues after the last event of its page, so paging by cursor returns each event exactly once even while events are being written, including each page's own audit event. offset, with X-Philter-Export-Next-Offset, is kept for paging a range that is no longer receiving events; on a range that includes the current day it can repeat events across pages. Give offset or cursor, not both. Timestamps in the CSV are UTC. The export is itself audited. Requires an administrator as well as the audit:read scope. Requires the `audit:read` scope.",
        "operationId": "exportAuditLog",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "zone",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The CSV. Headers give the row count, whether it was truncated, and the time zone used.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Export-Next-Cursor": {
                "description": "The cursor of the next page. Present only when the export was truncated.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Export-Next-Offset": {
                "description": "The offset of the next page. Present only when an export read by offset was truncated.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Export-Rows": {
                "description": "How many events the export holds.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Export-Time-Zone": {
                "description": "The time zone the from and to dates were read in.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Export-Truncated": {
                "description": "Whether the row cap cut the export short.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "A date is missing or not YYYY-MM-DD, the range is reversed or longer than the maximum, the zone is not a time zone, offset is negative or not a number, the cursor is not one from an export page, both offset and cursor were given, or limit is not a number. The body says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold audit:read, or the caller is not an administrator.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "object"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts": {
      "get": {
        "tags": [
          "Contexts"
        ],
        "summary": "List existing contexts.",
        "description": "Returns the caller's contexts, paged, each with its name and when it was created. Admins may list another user's contexts with owner, or every user's with all_users=true, which also names each context's owner and requires ADMIN_CROSS_USER_ACCESS_ENABLED. Requires the `contexts:read` scope.",
        "operationId": "getContexts",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "all_users",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetContextsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Both owner and all_users were given.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it; or all_users was given by a caller who is not an administrator, or with cross-user access disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Contexts"
        ],
        "summary": "Create a context.",
        "description": "Creates a context from the name and settings in the body. A setting left out takes its default. A user can have at most 10 contexts, counting default. Requires the `contexts:write` scope.",
        "operationId": "createContext",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContextRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The context was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The name is missing or blank, or cannot contain /, \\, ;, %, or control characters, and cannot be . or .., since it must be usable in a request path; disambiguationScope is not document or context; or the name or a setting was given as a query parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The context was not created, and reason says why: context_exists (the caller already has a context with this name) or context_limit_reached (the caller already has the most contexts a user may have).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContextConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts/{name}": {
      "delete": {
        "tags": [
          "Contexts"
        ],
        "summary": "Delete a context.",
        "description": "Delete an existing context. Requires the `contexts:write` scope.",
        "operationId": "deleteContext",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The context was deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no context with that name, or the owner does not exist or the caller may not reach it. The body carries a message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The context has open asynchronous redaction jobs and cannot be deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Contexts"
        ],
        "summary": "Get the details of a context.",
        "description": "Get the details of a context with the provided name: its size and its entries counted by filter type. The counts are computed in the database and sum to the size, with entries that have no filter type counted as untyped. Requires the `contexts:read` scope.",
        "operationId": "getContext",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetContextResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "A context with the given name does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Contexts"
        ],
        "summary": "Update a context's settings.",
        "description": "Changes a context's entityTypeDisambiguation, disambiguationScope, and ledger settings, given in the body. Only the settings given change; one left out keeps its current value. Recorded as a context_updated audit event naming each setting that changed. Requires the `contexts:write` scope.",
        "operationId": "updateContext",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContextRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The context was updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "No setting was given, disambiguationScope is not document or context, or a setting was given as a query parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Context not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts/{name}/entries": {
      "delete": {
        "tags": [
          "Contexts"
        ],
        "summary": "Empty all entries from a context.",
        "description": "Requires the `contexts:write` scope.",
        "operationId": "emptyEntries",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The context was emptied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Contexts"
        ],
        "summary": "List entries within a context.",
        "description": "Requires the `contexts:read` scope.",
        "operationId": "listEntries",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetContextEntriesResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Context not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts/{name}/entries/export": {
      "get": {
        "tags": [
          "Contexts"
        ],
        "summary": "Export a context's mapping table.",
        "description": "Returns the complete token-to-replacement mapping table for a context in a portable JSON form that can be re-imported into another context or environment. Only token hashes (never the original tokens) and their replacements are returned. Requires the `contexts:read` scope.",
        "operationId": "exportEntries",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContextEntriesExport"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Context not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts/{name}/entries/import": {
      "post": {
        "tags": [
          "Contexts"
        ],
        "summary": "Import a mapping table into a context.",
        "description": "Imports token-to-replacement mappings (as produced by the export endpoint) into a context. By default an incoming token that already exists in the context is skipped; pass on_conflict=overwrite to replace existing replacements instead. Token hashes are keyed per username, so mappings match only when exported from a context of the same username under the same PHILTER_ENCRYPTION_KEY. Requires the `contexts:write` scope.",
        "operationId": "importEntries",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "on_conflict",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "skip"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContextEntriesExport"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportContextEntriesResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The payload or on_conflict value is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Context not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/contexts/{name}/entries/{entryId}": {
      "delete": {
        "tags": [
          "Contexts"
        ],
        "summary": "Delete a single context entry by id.",
        "description": "Requires the `contexts:write` scope.",
        "operationId": "deleteEntry",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "entryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The entry was deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/documents": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "List documents submitted for async redaction.",
        "description": "Lists the caller's submitted documents, whatever their status, each with its type, file name, status, submission time, and policy. q filters to documents whose document ID or file name contains it, ignoring case; status filters by status. Requires the `documents:read` scope.",
        "operationId": "listDocuments",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetDocumentsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/documents/{documentId}": {
      "delete": {
        "tags": [
          "Documents"
        ],
        "summary": "Delete an async redaction record.",
        "description": "Requires the `documents:write` scope.",
        "operationId": "deleteDocument",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The record and any stored redacted bytes were deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Unknown document id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The document is processing, or notification reconciliation is pending.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "example": 5,
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Download the redacted document.",
        "description": "Requires the `documents:read` scope.",
        "operationId": "downloadDocument",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Completed result bytes.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Unknown document id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "Redaction not yet complete.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "byte"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "410": {
            "description": "Redaction failed.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "byte"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/documents/{documentId}/status": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Get the status of an async redaction.",
        "description": "Returns the status, any error, the effective configuration hash, and the policy the document was submitted with: policyName, policyVersion, and policyContentHash. Read that revision with GET /api/policies/{policyName}/versions/{revision}. Requires the `documents:read` scope.",
        "operationId": "getStatus",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetRedactionStatusResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Unknown document id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/explain": {
      "post": {
        "tags": [
          "Explain"
        ],
        "summary": "Redact text and explain the redactions.",
        "description": "Redacts the plain-text request body using the given policy and context, returning the filtered text together with an explanation of every span that was redacted (type, position, and applied filter). Use the c query parameter to select a context and p to select a policy. When output signing is enabled in Admin Settings, the response includes an `X-Philter-Signature` header containing an ES256 JWT that binds the SHA-256 hash of the response body, the applied policy name and version, a per-response UUID, and an issue timestamp. Verify the signature using the public key from `GET /api/signing-key`. Pass sign=true to request a signature when the deployment has not enabled signing. The admin setting is a floor: with it on every response is signed and sign=false does not change that. Requires the `redact` scope.",
        "operationId": "explainTextPlainAsApplicationJson",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "c",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": ""
            }
          },
          {
            "name": "p",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default"
            }
          },
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sign",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Full text filter result with governing policy metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "context": {
                      "type": "string"
                    },
                    "explanation": {
                      "type": "object",
                      "properties": {
                        "appliedSpans": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ExplanationSpan"
                          }
                        },
                        "identifiedSpans": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ExplanationSpan"
                          }
                        }
                      }
                    },
                    "filteredText": {
                      "type": "string"
                    },
                    "piece": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "policyContentHash": {
                      "type": "string"
                    },
                    "policyName": {
                      "type": "string"
                    },
                    "policyVersion": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "tokens": {
                      "type": "integer",
                      "format": "int32"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Document-Id": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Signature": {
                "description": "An ES256 JWT binding the SHA-256 hash of the response body. Present when output signing is enabled or the request asked for it with sign=true; verify it with GET /api/signing-key.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "405": {
            "description": "Method Not Allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Signing is enabled but the signing operation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/filter": {
      "post": {
        "tags": [
          "Filter"
        ],
        "summary": "Redact text or a PDF.",
        "description": "Requires the `redact` scope. Text is synchronous and returns text/plain. For PDFs select application/pdf or application/zip with Accept. PDFs default to async=true, returning application/json with documentId regardless of the selected download format. Set async=false for inline binary output. Async submission captures resolved configuration. Pass sign=true to request a signature when the deployment has not enabled signing. The admin setting is a floor: with it on every response is signed and sign=false does not change that. It applies to the text path, which is the one that is signed; sign=true on a PDF request is refused with 400 rather than answered unsigned.",
        "operationId": "filter",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "c",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": ""
            }
          },
          {
            "name": "p",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default"
            }
          },
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sign",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "async",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "description": "Optional, asynchronous requests only. Documents queued by one account with different fairness keys take turns, as different accounts do. The key is opaque to Philter: use an internal identifier, such as a user or group ID, and never personal data. 1 to 64 characters of letters, digits, '.', '_', ':' and '-'.",
            "name": "fairness_key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/pdf": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Redacted text or synchronous binary result.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "X-Document-Id": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Hash": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Name": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Signature": {
                "description": "An ES256 JWT binding the SHA-256 hash of the response body. Present when output signing is enabled or the request asked for it with sign=true; verify it with GET /api/signing-key. Only text responses are signed.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "202": {
            "description": "PDF accepted; poll status and download the result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documentId": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Effective-Configuration-SHA256": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Hash": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Name": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Philter-Policy-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Invalid request, malformed or unparseable PDF, password-protected PDF, PDF without pages, or sign=true on a PDF request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The named policy does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "405": {
            "description": "Method Not Allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Request body, resolved configuration (1 MiB), or encrypted async record exceeds its size limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "429": {
            "description": "Account async queue capacity reached.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "example": 5,
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Signing is enabled but the signing operation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Global queue capacity reached, admission contention wait (500 ms) exhausted, or recovery required.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "example": 5,
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "security": [],
        "tags": [
          "Health"
        ],
        "summary": "Get the health of Philter, including the supported redaction policy schema version.",
        "description": "Unauthenticated endpoint returning health, the application version, the supported redaction policy schema version, and the build's git commit. The status is \"UP\" when Philter is healthy.",
        "operationId": "status",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/holds": {
      "get": {
        "tags": [
          "Legal Holds"
        ],
        "summary": "List legal holds.",
        "description": "Returns the caller's active legal holds, paged and ordered by set date descending. Admins may list another user's holds via the owner parameter, or every user's with all_users=true, which adds each hold's owner and requires ADMIN_CROSS_USER_ACCESS_ENABLED. Requires the `holds:read` scope.",
        "operationId": "listHolds",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "all_users",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of legal holds in holds, most recently set first by default, and the total. With all_users, each hold also has an owner field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetHoldsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Both owner and all_users were given.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Legal Holds"
        ],
        "summary": "Set a legal hold.",
        "description": "Creates a named hold that blocks deletion and purge of the specified evidence until the hold is released. The reference must be unique for the calling user. Two scope types are supported: 'document_chain' protects a specific document's ledger chain; 'user' protects all governance evidence owned by the hold's owner, which is the caller or the user named by owner. For a user hold, scopeValue is optional; if given it must be the owner's username, and the owner's username is stored either way. Admins may place a hold on another user's evidence via the owner parameter. Requires the `holds:write` scope.",
        "operationId": "setHold",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegalHoldRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The hold was set and is now active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Required fields are missing, the scope type is invalid, or the reference cannot contain /, \\, ;, %, or control characters, and cannot be . or .., since it must be usable in a request path.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The hold was not set, and reason says why: hold_exists (a hold with this reference exists) or operation_in_progress (an evidence or hold operation for the owner is active or requires recovery).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/holds/{reference}": {
      "delete": {
        "tags": [
          "Legal Holds"
        ],
        "summary": "Release a legal hold.",
        "description": "Removes the hold with the given reference. Once released, evidence previously covered by this hold may become eligible for deletion or purge if no other holds remain. Releasing a hold is audited. Only the user who set the hold, or an administrator, may release it. Admins may release another user's hold via the owner parameter. Requires the `holds:write` scope.",
        "operationId": "releaseHold",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The hold was released.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The caller neither set the hold nor is an administrator; reason is admin_required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "No hold with the given reference exists for this user, or the owner does not exist or may not be reached. An unreachable owner gets the same not_found body as anything else not found, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The hold was not released because an evidence or hold operation for the owner is active or requires recovery; reason is operation_in_progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Legal Holds"
        ],
        "summary": "Get a legal hold.",
        "description": "Returns the hold with the given reference. Admins may retrieve another user's hold via the owner parameter. Requires the `holds:read` scope.",
        "operationId": "getHold",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The hold details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "No hold with the given reference exists for this user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/ledger": {
      "delete": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "Purge old ledger entries.",
        "description": "Deletes whole completed chains whose completion and newest entry are older than the given number of days. The ledger is kept indefinitely by default, so this is how stale entries are pruned on demand. Requires an administrator and LEDGER_DELETION_ENABLED=true. Admins may purge another user's entries by passing that user's username as owner, which additionally requires ADMIN_CROSS_USER_ACCESS_ENABLED=true. Requires the `ledger:delete` scope.",
        "operationId": "purge",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "older_than_days",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching entries were purged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "older_than_days is missing or negative.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The caller is not an administrator, or ledger deletion is disabled for this deployment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "An evidence or hold operation is active or requires recovery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "423": {
            "description": "One or more active legal holds protect entries in this user's ledger. Release all holds before purging.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "List redaction-ledger chains.",
        "description": "Returns the head (genesis entry) of each redacted document's ledger chain, most recent first. Pass q to filter by document id or filename. Admins may list another user's chains by passing that user's username as owner, or every user's with all_users=true, which adds each chain's owner, cannot be combined with q, and requires ADMIN_CROSS_USER_ACCESS_ENABLED. Requires the `ledger:read` scope.",
        "operationId": "getLedger",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "all_users",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger chain heads; with all_users, each also names its owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetLedgerResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "all_users was combined with owner or q.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/ledger/{documentId}": {
      "delete": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "Delete a document's ledger chain.",
        "description": "Permanently deletes every ledger entry for a completed document chain. Active or failed publication returns 409. Requires an administrator and LEDGER_DELETION_ENABLED=true. Admins may delete another user's chain by passing that user's username as owner, which additionally requires ADMIN_CROSS_USER_ACCESS_ENABLED=true. Requires the `ledger:delete` scope.",
        "operationId": "deleteChain",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The document's ledger chain was deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The caller is not an administrator, or ledger deletion is disabled for this deployment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "No ledger chain for that document exists for this user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "An evidence or hold operation is active or requires recovery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "423": {
            "description": "The chain is protected by an active legal hold. Release the hold before deleting.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "Get a document's ledger chain.",
        "description": "Returns a page of a document's ledger entries, in chain order, with how many the chain has and whether the whole chain currently verifies. Page with offset and limit; read every entry at once with the export. A chain that cannot be validated, such as one with an entry that can no longer be read, returns valid false with a validationError and no entries. Requires the `ledger:read` scope.",
        "operationId": "getChain",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerChainResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "405": {
            "description": "Method Not Allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/ledger/{documentId}/export": {
      "get": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "Export a document's ledger chain.",
        "description": "Returns the full ledger chain for a document as a portable JSON document that can be archived and later re-verified. The export contains the decrypted token and replacement values, so treat it as sensitive. A chain with an entry that cannot be read is not exported, even in part. Requires the `ledger:export` scope.",
        "operationId": "exportChain",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerExport"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "422": {
            "description": "An entry in the chain could not be read, so the chain is not exported; reason is entry_unreadable. The attempt is audited as redaction_ledger_exported with refused in its details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerRefusalResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/ledger/{documentId}/valid": {
      "get": {
        "tags": [
          "Redaction Ledger"
        ],
        "summary": "Verify a document's ledger chain.",
        "description": "Returns whether the hash chain for the document's ledger verifies (no entry has been altered and every link is intact). A chain that cannot be validated returns valid false with a validationError. Requires the `ledger:read` scope.",
        "operationId": "validateChain",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerChainResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "405": {
            "description": "Method Not Allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/limits": {
      "get": {
        "tags": [
          "Limits"
        ],
        "summary": "Get Philter's limits and the caller's capabilities.",
        "description": "Returns the sizes, lengths, and name rules Philter enforces, the session key timeouts, and what the caller may do: their role, how many contexts they have, and whether they may reach other users' resources or delete ledger chains. Each value is the one Philter enforces, including any set through its environment variable. Any key may call it, whatever its scopes, including a session key that must first change its password or enroll in MFA, since it describes the API and the caller's own access. For an administrator, crossUserAccess and ledgerDeletion reflect ADMIN_CROSS_USER_ACCESS_ENABLED and LEDGER_DELETION_ENABLED; for anyone else they are false, so the response does not reveal those settings.",
        "operationId": "getLimits",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The limits and the caller's capabilities.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/lists": {
      "get": {
        "tags": [
          "Custom Lists"
        ],
        "summary": "List custom lists.",
        "description": "Lists the caller's custom lists, all of them, each with its name, description, and number of items. Admins may list another user's lists with owner, or every user's with all_users=true, which is paged with offset and limit, adds each list's owner, and requires ADMIN_CROSS_USER_ACCESS_ENABLED. Requires the `lists:read` scope.",
        "operationId": "getLists_1",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "all_users",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of lists, each with its name, description, and size (with all_users, its owner too), and the total.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetCustomListsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Both owner and all_users were given.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts. Also returned for all_users when the caller is not an administrator or cross-user access is disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/lists/{name}": {
      "delete": {
        "tags": [
          "Custom Lists"
        ],
        "summary": "Delete a list.",
        "description": "Delete a list with the given name. Requires the `lists:write` scope.",
        "operationId": "deleteList",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The list was deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The given list does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Custom Lists"
        ],
        "summary": "Get the contents of a list.",
        "description": "Gets a list's items and description. Requires the `lists:read` scope.",
        "operationId": "getLists",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomListResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The list does not exist, or the owner does not exist or may not be reached. The API does not distinguish these, so a name or owner cannot be used to discover what exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Custom Lists"
        ],
        "summary": "Create a list.",
        "description": "Creates a list from the request body's items and optional description, which defaults to an empty string. A name the owner already uses is refused with 409; replace an existing list with PUT /api/lists/{name}. Requires the `lists:write` scope.",
        "operationId": "createList",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomListRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The list was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The list name is empty or cannot contain /, \\, ;, %, or control characters, and cannot be . or .., the body is not a list with items, the list contains too many items (maximum 100), an item is too long (maximum 50 characters), the description is too long (maximum 200 characters), or the request has a description parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The list was not created because the owner already has a list with this name; reason is list_exists. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomListConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Custom Lists"
        ],
        "summary": "Replace a list.",
        "description": "Replaces the items of an existing list with the request body's items. description is optional: left out, the list keeps its description; given as an empty string, it is cleared. Requires the `lists:write` scope.",
        "operationId": "replaceList",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The list was replaced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The body is not a list with items, the list contains too many items (maximum 100), an item is too long (maximum 50 characters), the description is too long (maximum 200 characters), or the request has a description parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such list, or the owner does not exist or the caller may not reach it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "List the local PhEye models.",
        "description": "Returns the models in the deployment's models directory (PHEYE_MODELS_DIRECTORY), ordered by name. A ph-eye filter uses one by giving its name as phEyeConfiguration.modelPath; the model then runs inside Philter. The directory is read on each request, so a model an administrator adds is listed without a restart. The models are the deployment's, the same for every user. Requires the `policies:read` scope.",
        "operationId": "getModels",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every model, which may be none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetModelsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/models/{modelName}": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "Get a local PhEye model.",
        "description": "Returns one model in the deployment's models directory by name. Requires the `policies:read` scope.",
        "operationId": "getModel",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "modelName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The model.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no model with the given name.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/pii-counts": {
      "get": {
        "tags": [
          "PII Counts"
        ],
        "summary": "List the recorded PII counts.",
        "description": "Returns the PII type counts Philter records when the diffuseCountsEnabled setting is on, for Philter Diffuse to privatize: one entry per user's context and UTC day, with a stable id, giving for each PII type how many redactions found at least one of it, and how many found any PII. Only counts, never any PII. Sorted with sort (day, the default, context, or documents) and order (asc or desc); the default is the most recent day first. Paginated. Another user's context name and username are returned only with ADMIN_CROSS_USER_ACCESS_ENABLED; their counts always are. Narrow by day with from and to (YYYY-MM-DD, UTC, both inclusive), by context name with context (only the caller's own without cross-user access), and by username with owner. recordingEnabled says whether counts are being recorded now, so an empty listing can be told apart from recording being off; counts recorded before it was turned off are still returned. Each read is audited. The counts cover the whole deployment, so this requires an administrator as well as the pii-counts:read scope. Requires the `pii-counts:read` scope.",
        "operationId": "getPiiCounts",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "context",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of counts, the total matching the filters, and whether counts are being recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPiiCountsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "A date is not YYYY-MM-DD or is outside the years 1 to 9999, or from is after to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold pii-counts:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "owner names no user, or another user without ADMIN_CROSS_USER_ACCESS_ENABLED.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies": {
      "get": {
        "tags": [
          "Policies"
        ],
        "summary": "List existing policies.",
        "description": "Returns the caller's policies, paged, each with its name, description, revision, and when it was created and last changed. Admins may list another user's policies by passing that user's username as owner, or every user's with all_users=true, which also names each policy's owner and requires ADMIN_CROSS_USER_ACCESS_ENABLED. With managed=true, returns the built-in managed policies instead. With deleted=true, returns the caller's (or owner's) deleted policies whose version history is kept, by name, each with its latest revision and when and by whom it was deleted; read a deleted policy's history with GET /api/policies/{policyName}/versions. Requires the `policies:read` scope.",
        "operationId": "getPolicyNames",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "all_users",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "managed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "deleted",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of policies in policies, and the total. With deleted, each item describes a deleted policy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPoliciesResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Both owner and all_users were given, managed was combined with either, or deleted was combined with all_users or managed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Policies"
        ],
        "summary": "Create a policy.",
        "description": "Creates a policy from the request body under the given name. A name the owner already uses is refused with 409; replace an existing policy with PUT /api/policies/{policyName}. The policy is validated before it is stored. To set the description and notes in the same write, pass details=true and send {\"policy\": <the policy>, \"description\": \"...\", \"notes\": \"...\"}; description and notes are optional, a field left out or null is left as it is, and an empty value clears it. Without details=true the body is the policy itself. Admins may create a policy in another user's account by passing that user's username as owner. Requires the `policies:write` scope.",
        "operationId": "create",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "details",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "description": "Native Phileas policy. identifiers is required; custom dictionaries use dictionaries.",
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The policy was created and is now active. The ETag header identifies it. A policy_activated audit event is recorded.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing or invalid, the policy is invalid, the request has a description or notes parameter, or, with details=true, the body is not a policy with details or the description or notes are too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The policy was not created because the owner already has a policy with this name; reason is policy_exists. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/compile": {
      "post": {
        "tags": [
          "Policies"
        ],
        "summary": "Compile a PhiSQL policy.",
        "description": "Compiles a policy authored in PhiSQL into the native Phileas policy format. The request body is PhiSQL source; the response carries the policy name and description and the compiled policy JSON, which can then be saved via POST /api/policies. Requires the `policies:read` scope.",
        "operationId": "compile",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Compiled native policy; does not save it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "description": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "policy": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The PhiSQL failed to parse/compile, or the compiled policy failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/templates/{templateName}": {
      "get": {
        "tags": [
          "Policies"
        ],
        "summary": "Get a policy template.",
        "description": "Returns a starting point for a new policy: native policy JSON that Philter accepts for the running policy schema version, to edit and save with POST /api/policies. The template named default is the policy every new user's default policy is created from. A template is not a policy and cannot be used to redact until it is saved as one. Requires the `policies:read` scope.",
        "operationId": "getTemplate",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "templateName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The template's native policy JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no template with the given name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}": {
      "delete": {
        "tags": [
          "Policies"
        ],
        "summary": "Delete a policy.",
        "description": "Deletes the policy with the given name. Admins may delete another user's policy by passing that user's username as owner. Requires the `policies:write` scope.",
        "operationId": "delete",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The policy was deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such policy, with a message. Also returned, saying only not found, when the owner does not exist or the caller may not reach it, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The policy was not deleted, and reason says why: policy_default (the default policy) or policy_managed (a managed policy).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Policies"
        ],
        "summary": "Get a policy.",
        "description": "Returns the full policy with the given name. A name starting with managed_ returns that built-in managed policy. Admins may retrieve another user's policy by passing that user's username as owner. The policy's description and notes are at /api/policies/{policyName}/details. The ETag header identifies the policy as read, together with its description and notes; send it back in If-Match when replacing the policy, setting its details, or rolling it back, so the change is refused if someone else changed the policy since. Requires the `policies:read` scope.",
        "operationId": "get",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Native policy JSON, preserving field names.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "A policy with the given name does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Policies"
        ],
        "summary": "Replace a policy.",
        "description": "Replaces an existing policy with the request body, as a new revision. The policy is validated before it is stored. Its description and notes are kept unless the body gives them. To set the description and notes in the same write, pass details=true and send {\"policy\": <the policy>, \"description\": \"...\", \"notes\": \"...\"}; description and notes are optional, a field left out or null is left as it is, and an empty value clears it. Without details=true the body is the policy itself. Send the ETag from your read in If-Match to make the write conditional: if the policy, its description, or its notes changed since, nothing is written and the response is 409 with reason policy_changed. Without If-Match the write is not checked against an earlier read. Admins may replace another user's policy by passing that user's username as owner. Requires the `policies:write` scope.",
        "operationId": "replace",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "details",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "description": "Native Phileas policy. identifiers is required; custom dictionaries use dictionaries.",
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The policy was replaced and is now active. The ETag header identifies it as written. A policy_activated audit event is recorded.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy is invalid, the request has a description or notes parameter, If-Match is not an entity tag this API returned, or, with details=true, the body is not a policy with details or the description or notes are too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such policy, with a message. Also returned, saying only not found, when the owner does not exist or the caller may not reach it, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The policy was not replaced, and reason says why: policy_managed (a managed policy, which cannot be replaced) or policy_changed (it changed since the read If-Match names, or concurrently; read it again and retry). Nothing was written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/copy": {
      "post": {
        "tags": [
          "Policies"
        ],
        "summary": "Copy a policy.",
        "description": "Creates a new policy named by name from one of the caller's policies or, for a name starting with managed_, from a built-in managed policy. The copy has the source's policy and description; a copy of a managed policy notes which one it came from, and a copy of the caller's own policy keeps its notes. The copy starts its own version history and is active at once. Admins may copy within another user's account by passing that user's username as owner. Requires the `policies:write` scope.",
        "operationId": "copy",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The copy's details. The ETag header identifies the copy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyDetailsResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The new name is missing or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such policy to copy, or the owner does not exist or the caller may not reach it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "A policy with the new name already exists; reason is policy_exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/details": {
      "get": {
        "tags": [
          "Policies"
        ],
        "summary": "Get a policy's details.",
        "description": "Returns a policy's name, description, notes, current revision, entity tag, whether it is a built-in managed policy, and when it was created and last updated: everything except the policy itself. The ETag header carries the entity tag too, for If-Match. A name starting with managed_ returns that managed policy's details. Admins may read another user's by passing that user's username as owner. Requires the `policies:read` scope.",
        "operationId": "getDetails",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The policy's details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyDetailsResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such policy, or the owner does not exist or the caller may not reach it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Policies"
        ],
        "summary": "Set a policy's description and notes.",
        "description": "Sets the description (up to 200 characters) and notes (up to 1000). A field left out is left as it is; an empty value clears it. They are not part of the policy, so this does not create a new version, and the revision stays the same, but the entity tag changes. Send the ETag from your read in If-Match to make the write conditional: if the policy, its description, or its notes changed since, nothing is written and the response is 409 with reason policy_changed. Without If-Match the write is not checked against an earlier read. Managed policies cannot be changed. Admins may change another user's by passing that user's username as owner. Requires the `policies:write` scope.",
        "operationId": "setDetails",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyDetailsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The policy's details after the change. The ETag header identifies them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyDetailsResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The description or notes are too long, or If-Match is not an entity tag this API returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no such policy, or the owner does not exist or the caller may not reach it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "Nothing was changed, and reason says why: policy_managed (a managed policy) or policy_changed (it changed since the read If-Match names, or concurrently; read it again and retry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/diff": {
      "get": {
        "tags": [
          "Policy Versions"
        ],
        "summary": "Diff two revisions of a policy.",
        "description": "Returns an RFC 6902 JSON Patch array describing what changed between the 'from' revision and the 'to' revision. If from and to are omitted, the two most recent retained revisions are compared. Object-level changes (adds, removes, replaces) are reported per field path; array values that differ are reported as a single replace at the array path. Each replace and remove also carries oldValue, the value before the change, so a client can show both sides; RFC 6902 has a client applying the patch ignore it. Admins may diff another user's policy via owner. Requires the `policies:read` scope.",
        "operationId": "diff",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revision diff; value is omitted for removals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "oldValue": {
                            "description": "The value before the change. Present on replace and remove."
                          },
                          "op": {
                            "type": "string",
                            "enum": [
                              "add",
                              "remove",
                              "replace"
                            ]
                          },
                          "path": {
                            "type": "string"
                          },
                          "value": {}
                        }
                      }
                    },
                    "from": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "to": {
                      "type": "integer",
                      "format": "int32"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing, fewer than two retained revisions exist for a default diff, or only one of from/to was supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The policy or a requested revision does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/rollback": {
      "post": {
        "tags": [
          "Policy Versions"
        ],
        "summary": "Roll back a policy to a prior revision.",
        "description": "Restores the content of the specified revision as a new head revision, preserving the full history. The live policy is updated with the prior content and its revision counter is incremented. The description and notes are kept. The rollback is audited. Send the ETag from your read in If-Match to make the write conditional: if the policy, its description, or its notes changed since, nothing is written and the response is 409 with reason policy_changed. Without If-Match the write is not checked against an earlier read. Admins may roll back another user's policy via the owner parameter. Requires the `policies:write` scope.",
        "operationId": "rollback",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Rollback succeeded. Body contains the new revision number, and the ETag header identifies the policy as rolled back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyRollbackResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name or target revision is missing, or If-Match is not an entity tag this API returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The policy or the target revision does not exist; the message says which. When the owner does not exist or the caller may not reach it, the body only says not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The policy was not rolled back, and reason says why: policy_managed (managed policies cannot be rolled back) or policy_changed (the policy changed since the read If-Match names, or concurrently; read it again and retry). Nothing was written.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/versions": {
      "get": {
        "tags": [
          "Policy Versions"
        ],
        "summary": "List retained versions of a policy.",
        "description": "Returns a summary of each retained snapshot for the named policy, ordered by revision descending (most recent first). Each entry carries the revision number, the timestamp the snapshot was captured, its content hash, and the username of the user whose change produced it. The history of a deleted policy is kept and listed the same way; GET /api/policies?deleted=true lists deleted policies. Admins may browse another user's history via the owner parameter. Requires the `policies:read` scope.",
        "operationId": "listVersions",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of version summaries, most recent first by default, and the total.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPolicyVersionsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The named policy does not exist, or a non-admin caller named another user as owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/policies/{policyName}/versions/{revision}": {
      "get": {
        "tags": [
          "Policy Versions"
        ],
        "summary": "Fetch a specific revision of a policy.",
        "description": "Returns the full policy JSON as it existed at the given revision. The response shape is identical to GET /api/policies/{policyName}. Admins may fetch another user's revision via the owner parameter. Requires the `policies:read` scope.",
        "operationId": "getVersion",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "policyName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "revision",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Native policy JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The policy name is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The policy or the requested revision does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/redact-lists": {
      "get": {
        "tags": [
          "Always/Never Redact Lists"
        ],
        "summary": "Get the always-redact and never-redact lists.",
        "description": "Returns the account's always-redact and never-redact lists, and each list's revision for If-Match on PUT /api/redact-lists/{list}. Both lists are always present; an account with no saved terms returns empty arrays. Admins may read another user's lists by passing that user's username as owner. Requires the `lists:read` scope.",
        "operationId": "getRedactLists",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactListsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Always/Never Redact Lists"
        ],
        "summary": "Replace the always-redact and never-redact lists.",
        "description": "Replaces both lists in full with the supplied contents - this is not a merge. Each field is the complete desired contents of that list; a list that is omitted or sent as an empty array is cleared. Terms are trimmed and blank entries are dropped. Each list may contain up to 1000 terms, and each term may be up to 100 characters. Both lists move to their next revision. Takes no If-Match; to replace one list on the condition that it has not changed, use PUT /api/redact-lists/{list}. Admins may replace another user's lists by passing that user's username as owner. Requires the `lists:write` scope.",
        "operationId": "replaceRedactLists",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedactListsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lists were replaced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request body is malformed, a list has too many terms, or a term is too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Always/Never Redact Lists"
        ],
        "summary": "Append to the always-redact and never-redact lists.",
        "description": "Appends the supplied terms to the existing lists rather than replacing them - use POST to replace. Each field's terms are added to the current contents of that list; a list that is omitted or sent as an empty array is left unchanged. Terms are trimmed, blank entries are dropped, and terms already present are not added again. The resulting list may contain up to 1000 terms, and each term may be up to 100 characters; an append that would exceed the limit is rejected. Each list appended to moves to its next revision. Admins may append to another user's lists by passing that user's username as owner. Requires the `lists:write` scope.",
        "operationId": "appendRedactLists",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedactListsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The terms were appended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request body is malformed, the resulting list has too many terms, or a term is too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/redact-lists/{list}": {
      "get": {
        "tags": [
          "Always/Never Redact Lists"
        ],
        "summary": "Get one redact list.",
        "description": "Returns one list, always or never, and its revision. The ETag header carries the revision too, quoted. Send it back in If-Match when replacing the list with PUT /api/redact-lists/{list}. A list never written is empty, at revision 0. Admins may read another user's list by passing that user's username as owner. Requires the `lists:read` scope.",
        "operationId": "getRedactList",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "list",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list and its revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactListResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The list is not always or never, or the owner does not exist or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Always/Never Redact Lists"
        ],
        "summary": "Replace one redact list.",
        "description": "Replaces one list, always or never, in full with the terms in the body, leaving the other list as it is, and moves it to its next revision. Send the revision you read in If-Match, quoted as the ETag gives it, so the write is refused with 409 and reason redact_list_changed if someone changed the list since; without If-Match the list is replaced whatever its revision. Terms are trimmed and blank entries are dropped. The list may contain up to 1000 terms, and each term may be up to 100 characters. Admins may replace another user's list by passing that user's username as owner. Requires the `lists:write` scope.",
        "operationId": "replaceRedactList",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "list",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedactListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The list was replaced. The body and the ETag header give its new revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactListResponse"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request body is malformed, the list has too many terms, a term is too long, or If-Match is not a revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The list is not always or never, or the owner does not exist or the caller may not reach it. The API does not distinguish the two, so an owner value cannot be used to discover accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The list is no longer at the revision given in If-Match. Nothing was written. Read the list again and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactListConflictResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/reidentify": {
      "post": {
        "tags": [
          "Re-identification"
        ],
        "summary": "Re-identify redacted values.",
        "description": "Reverses one or more values that were redacted via the CRYPTO_REPLACE or FPE_ENCRYPT_REPLACE strategy. The caller must supply a stated reason, which is recorded in the audit log alongside the actor, timestamp, and count of reversals. A user may re-identify their own values; an admin may re-identify any user's values by supplying that user's username via the owner parameter. CRYPTO_REPLACE requires policyName so the key can be resolved from the stored policy. FPE_ENCRYPT_REPLACE uses the user's per-account FPE key; if the policy specified a custom FPE configuration, also pass policyName so its key and tweak are used instead. Requires the `reidentify` scope.",
        "operationId": "reidentify",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReidentifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReidentifyResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Missing or invalid request fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Named policy not found, or owner not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/reidentify/text": {
      "post": {
        "tags": [
          "Re-identification"
        ],
        "summary": "Re-identify the CRYPTO_REPLACE values in a text.",
        "description": "Finds every CRYPTO_REPLACE token ({{...}}) in the text, decrypts it with the crypto key of the named policy, and returns the text with each token that decrypted replaced by its original value. A token that does not decrypt, because it was altered or encrypted under another key, is left as it is and counted as failed. FPE_ENCRYPT_REPLACE and other strategies are not restored. The caller must supply a stated reason, which is recorded in the audit log with the tokens and the counts. An admin may act for another user by supplying that user's username via the owner parameter. Requires the `reidentify` scope.",
        "operationId": "reidentifyText",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReidentifyTextRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReidentifyTextResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Missing or invalid request fields, or the policy has no crypto key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "Insufficient scope, disabled API, or access denied.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Named policy not found, or owner not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Get the admin settings.",
        "description": "Returns the deployment's admin settings. The Phield API key is never returned, only whether one is set. Also returns, read-only, whether cross-user access and ledger deletion are enabled and whether the signing key is externally managed, which are set by environment variables. Requires an administrator as well as the scope. Requires the `settings:read` scope.",
        "operationId": "getSettings",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold settings:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Change admin settings.",
        "description": "Changes the settings given in the body and leaves the rest as they are. An empty phieldApiKey removes the key. phieldUrl must be an absolute http or https URL, and is required while Phield is enabled; both are checked when a request changes Phield. Each webhookAllowlist entry must be a hostname, an IP address, or a CIDR range. Nothing is changed if any value is invalid. Returns the settings as saved, with warnings, such as a Phield API key that will be sent over http. Recorded as a settings_updated audit event naming the settings that changed and the calling API key, not their values. The read-only crossUserAccessEnabled, ledgerDeletionEnabled, and signingKeyExternallyManaged are ignored. Requires an administrator as well as the scope. Requires the `settings:write` scope.",
        "operationId": "updateSettings",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSettingsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The settings as saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "A value is invalid. The body says which, and nothing was changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold settings:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/sign-in": {
      "get": {
        "security": [],
        "tags": [
          "Sign-in"
        ],
        "summary": "Get the sign-in options.",
        "description": "Tells a sign-in page, before anyone has signed in, that password sign-in is enabled, and gives the rules a password must meet, for a page where a person sets one. Requires no API key. Returns 404 unless PASSWORD_SIGN_IN_ENABLED is true, as the other sign-in endpoints do, so a 404 means password sign-in is not available.",
        "operationId": "getSignInOptions",
        "responses": {
          "200": {
            "description": "Password sign-in is enabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInOptionsResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Password sign-in is not enabled.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "security": [],
        "tags": [
          "Sign-in"
        ],
        "summary": "Sign in.",
        "description": "Takes a username and password. For a user enrolled in MFA, returns a single-use challenge that expires in five minutes; send it with a code to POST /api/sign-in/mfa to get the key. Otherwise returns a session key for the user, which expires after SESSION_KEY_IDLE_TIMEOUT_MINUTES without a request or SESSION_KEY_MAX_LIFETIME_MINUTES after issue. The key holds every scope, with the user's role still deciding administrator access, but cannot create API keys. A user who must change their password, or must enroll in MFA, gets a key that can only do that and sign out. A wrong password, an unknown user, a user without a password, and a deactivated user all get the same 401. Requires no API key. Returns 404 unless PASSWORD_SIGN_IN_ENABLED is true. Recorded as sign_in_succeeded or sign_in_failed with the username and client IP address, never the password.",
        "operationId": "signIn",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignInRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in, or, for a user enrolled in MFA, a challenge. A session key is returned here and nowhere else.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/SignInResponse"
                    },
                    {
                      "$ref": "#/components/schemas/SignInChallengeResponse"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The username or password is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The password is right, but the user's MFA is locked after repeated bad codes. An administrator must unlock it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Password sign-in is not enabled.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "429": {
            "description": "The username is locked after repeated failures (POST /api/sign-in only), or the client address is over SIGN_IN_RATE_LIMIT_PER_MINUTE. Retry-After gives the seconds to wait, and reason says which: locked or rate_limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInThrottledResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/sign-in/mfa": {
      "post": {
        "security": [],
        "tags": [
          "Sign-in"
        ],
        "summary": "Complete sign-in with an MFA code.",
        "description": "Takes the challenge POST /api/sign-in returned and a code from the user's authenticator app, and returns the session key. The challenge is used up by any attempt, right or wrong, so a wrong code means signing in again. Each code is accepted once. The fifth consecutive bad code locks the user until an administrator unlocks them. Requires no API key. Returns 404 unless PASSWORD_SIGN_IN_ENABLED is true. Recorded as sign_in_succeeded or sign_in_failed.",
        "operationId": "signInMfa",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignInMfaRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in. The session key is returned here and nowhere else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The challenge is unknown, used, or expired, or the code is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The user's MFA is locked after repeated bad codes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Password sign-in is not enabled.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "429": {
            "description": "The username is locked after repeated failures (POST /api/sign-in only), or the client address is over SIGN_IN_RATE_LIMIT_PER_MINUTE. Retry-After gives the seconds to wait, and reason says which: locked or rate_limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInThrottledResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/signing-key": {
      "get": {
        "security": [],
        "tags": [
          "Signing"
        ],
        "summary": "Get the public signing key.",
        "description": "Returns the operator's ES256 (ECDSA P-256) public signing key in PEM, JWK, and fingerprint form. Use the public key to verify the `X-Philter-Signature` JWT header returned on successful `POST /api/filter` and `POST /api/explain` responses when output signing is enabled. This endpoint does not require authentication.",
        "operationId": "getSigningKey",
        "responses": {
          "200": {
            "description": "Active public signing key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fingerprint": {
                      "type": "string"
                    },
                    "jwk": {
                      "type": "object",
                      "properties": {
                        "crv": {
                          "example": "P-256",
                          "type": "string"
                        },
                        "kid": {
                          "type": "string"
                        },
                        "kty": {
                          "example": "EC",
                          "type": "string"
                        },
                        "x": {
                          "type": "string"
                        },
                        "y": {
                          "type": "string"
                        }
                      }
                    },
                    "keyId": {
                      "type": "string"
                    },
                    "pem": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/signing-key/regenerate": {
      "post": {
        "tags": [
          "Signing"
        ],
        "summary": "Rotate the output signing key.",
        "description": "Generates a new ES256 keypair and makes it the active signing key. The superseded key is retained and stays retrievable through GET /api/signing-key/{keyId}, so signatures and ledger entries made with it remain verifiable; consumers should resolve each signature's key id rather than caching one key. Rotation is the response to a suspected key compromise, so it requires an administrator as well as the signing:write scope, and is recorded as a signing_key_regenerated audit event. Requires the `signing:write` scope.",
        "operationId": "regenerateSigningKey",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The key that is now active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keyId": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold signing:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The signing key is managed by PHILTER_SIGNING_KEY_PATH and cannot be rotated through the API.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/signing-key/{keyId}": {
      "get": {
        "security": [],
        "tags": [
          "Signing"
        ],
        "summary": "Get a specific public signing key.",
        "description": "Returns a retained public signing key by its id, in PEM form. A key that has been superseded by a regeneration is kept, because redaction-ledger entries signed with it must remain verifiable. Ledger entries and exports name the key that signed them in their signingKeyId field. This endpoint does not require authentication.",
        "operationId": "getSigningKeyById",
        "parameters": [
          {
            "name": "keyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retained public signing key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "active": {
                      "type": "boolean"
                    },
                    "keyId": {
                      "type": "string"
                    },
                    "pem": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "No key with that id is retained.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "List users.",
        "description": "Lists users sorted by username, including deactivated users, with the total count. Requires an administrator as well as the scope. Requires the `users:read` scope.",
        "operationId": "getUsers",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "role",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of users.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetUsersResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Create a user.",
        "description": "Creates a user with a default policy and context. The role is user unless admin is given. A password is optional; without one the user can only use API keys, created with POST /api/users/{username}/api-keys. A user created with a password must change it at next sign-in, and setting it is audited as user_password_set. Requires an administrator as well as the scope. Recorded as a user_created audit event naming the calling administrator as the principal and the calling API key in the details. Requires the `users:write` scope.",
        "operationId": "createUser",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The user was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedUserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The username is missing, is reserved, or is not usable in a request path (it cannot contain /, \\, ;, %, or control characters, and cannot be . or ..), the role is not user or admin, or the password is shorter than 16 characters or longer than 72 bytes in UTF-8, or the email address is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "A user with that username already exists, active or deactivated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Get the calling key's user.",
        "description": "Returns the user that owns the API key making the request, with whether this deployment makes MFA available and required. Does not require an administrator. Requires the `users:read` scope.",
        "operationId": "getCurrentUser",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The calling key's user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentUserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me/mfa": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Start MFA enrollment.",
        "description": "Generates a TOTP secret for the calling key's own user and returns it, with an otpauth:// URI to show as a QR code. The secret is returned only here and is encrypted at rest. Enrollment takes effect only once POST /api/users/me/mfa/confirm sees a valid code; starting again replaces an unconfirmed secret. Takes the user's current password, so a stolen key cannot enroll an authenticator of its own; a wrong password counts toward the sign-in lockout. Requires the mfaAvailable setting, and the scope but not an administrator. Requires the `users:write` scope.",
        "operationId": "startMfaEnrollment",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartMfaEnrollmentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The secret to set up an authenticator app with.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MfaEnrollmentResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The password is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the password is not correct (reason wrong_password).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "MFA is not available on this deployment, the user is already enrolled, or the user has no password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "429": {
            "description": "The user's sign-in is locked after repeated failures; reason is locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInThrottledResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me/mfa/confirm": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Confirm MFA enrollment.",
        "description": "Completes enrollment with a code from the authenticator app for the secret POST /api/users/me/mfa returned. From then on, sign-in asks for a code. Revokes the user's session keys, so the person signs in again with MFA. Requires the scope but not an administrator. Recorded as a user_mfa_enrolled audit event. Requires the `users:write` scope.",
        "operationId": "confirmMfaEnrollment",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MfaCodeRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "MFA is enrolled.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The code is missing or not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "MFA is not available, the user is already enrolled, or no enrollment was started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me/mfa/remove": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Remove your own MFA enrollment.",
        "description": "Removes the calling key's own user's MFA, which takes a valid code so a stolen session key cannot turn MFA off. A bad code counts toward the lock. A user who has lost the device asks an administrator, who uses DELETE /api/users/{username}/mfa. Requires the scope but not an administrator. Recorded as a user_mfa_removed audit event. Requires the `users:write` scope.",
        "operationId": "removeOwnMfa",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MfaCodeRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "MFA was removed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The code is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, the code is not valid, or the user's MFA is locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is not enrolled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me/password": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Change the calling user's password.",
        "description": "Changes the password of the calling key's own user, which requires the current password. The new password must differ from it. Clears any required change and revokes the user's session keys, including the calling key if it is one; long-lived keys are unaffected. Requires the scope, not an administrator. Recorded as a user_password_changed audit event, without either password. A wrong current password counts toward the user's sign-in lockout, so a key cannot be used to guess the password; while the user is locked, the change is refused with 429. Requires the `users:write` scope.",
        "operationId": "changeOwnPassword",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The password was changed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "A password is missing, the new password is shorter than 16 characters or longer than 72 bytes in UTF-8, or it is the same as the current one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the current password is not correct.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user has no password yet, so an administrator sets the first one, or the password was changed by a concurrent request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "429": {
            "description": "The user is locked after repeated wrong passwords, at sign-in or here. Retry-After gives the seconds to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignInThrottledResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/me/session-keys": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Sign out every other session.",
        "description": "Revokes every session key the caller's user holds except the key making the request, so a person can sign out the sessions they do not recognize and stay signed in where they are. Called with a long-lived key, it revokes all of the user's session keys. Long-lived keys are unaffected. Each key is recorded as an api_key_deleted audit event with the reason signed out other sessions. Requires the `api-keys:write` scope.",
        "operationId": "revokeOtherSessionKeys",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "How many session keys were revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevokedSessionKeysResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Get a user.",
        "description": "Returns one user by username, active or deactivated. Requires an administrator as well as the scope. Requires the `users:read` scope.",
        "operationId": "getUser",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List a user's API keys.",
        "description": "Lists the named user's active keys, oldest first, with the total. session=true lists only session keys and session=false only long-lived keys. Requires an administrator as well as the scope. Requires the `api-keys:read` scope.",
        "operationId": "getUserApiKeys",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          },
          {
            "description": "true lists only session keys, false only long-lived keys; left out, both. total counts only the keys listed.",
            "name": "session",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetApiKeysResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:read, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Create an API key for a user.",
        "description": "Mints a key for the named user with the scopes given in the body, and returns its value once: only the hash is stored, so the response is the one chance to capture it. The scopes must be a subset of those the calling key holds, so this cannot be used to widen access beyond the credential making the request. Requires an administrator as well as the scope. A key for another user who already has, or has had, a key (including a session key) also requires ADMIN_CROSS_USER_ACCESS_ENABLED, and is recorded as admin_cross_user_access as well; without it an administrator can create only a user's first key, to provision them. Recorded as an api_key_created audit event naming the calling user and API key. Requires the `api-keys:write` scope.",
        "operationId": "createApiKey",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key was created. The value is returned here and nowhere else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKeyResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "No scopes were given, or one of them is not a scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write, is a session key, the caller is not an administrator, a requested scope is not held by the calling key, or the user already has or has had a key and ADMIN_CROSS_USER_ACCESS_ENABLED is off.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no active user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/deactivate": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Deactivate a user.",
        "description": "Deactivates a user: the user's API keys stop working, and the user and all of its data are retained so it can be reactivated. The last active administrator, and the calling administrator, cannot be deactivated. Requires an administrator as well as the scope. Recorded as a user_deactivated audit event naming the calling administrator as the principal and the calling API key in the details. Requires the `users:write` scope.",
        "operationId": "deactivateUser",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The user was deactivated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is already deactivated, is the caller, or is the last active administrator, or, with MongoDB storage, another change to the administrators is in progress (reason admin_change_in_progress; Retry-After gives the seconds to wait).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Present only with reason admin_change_in_progress.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/email": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Set a user's email address.",
        "description": "Sets or removes a user's email address. An address must have one @, something on each side, a dot in the domain, and no whitespace, and be at most 254 characters; a null or empty email removes it. The email address is not how a user is addressed or signs in, so changing it changes nothing else. Requires an administrator as well as the scope. Recorded as a user_email_changed audit event naming the calling administrator as the principal and the calling API key in the details; the address itself is not recorded. Requires the `users:write` scope.",
        "operationId": "setUserEmail",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetUserEmailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The email address was set or removed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The email address is not valid or too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/mfa": {
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Remove a user's MFA enrollment.",
        "description": "Removes another user's MFA, for a user who has lost their device, and clears any lock. On the caller's own user, use POST /api/users/me/mfa/remove, which takes a code. Requires an administrator as well as the scope. Recorded as a user_mfa_removed audit event naming the calling administrator and API key. Requires the `users:write` scope.",
        "operationId": "removeMfa",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "MFA was removed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is the caller, or is not enrolled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/mfa/unlock": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Unlock a user's MFA.",
        "description": "Unlocks a user locked after five consecutive bad MFA codes and resets the count. Requires an administrator as well as the scope. Recorded as a user_mfa_unlocked audit event naming the calling administrator and API key. Requires the `users:write` scope.",
        "operationId": "unlockMfa",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The user was unlocked.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is not locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/password": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Set or reset a user's password.",
        "description": "Sets another user's password without the current one. The user must change it at next sign-in, and their session keys are revoked. On the calling administrator's own user, this sets only the first password, with no change required, which is how the admin user gets one with the bootstrap API key; after that, use PUT /api/users/me/password. Requires an administrator as well as the scope. Recorded as a user_password_reset audit event when it replaces a password, or user_password_set when the user had none, naming the calling administrator and API key, never the password. Requires the `users:write` scope.",
        "operationId": "setPassword",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetPasswordRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The password was set.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The password is missing, shorter than 16 characters, or longer than 72 bytes in UTF-8.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is the caller and already has a password, or the password was changed by a concurrent request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/reactivate": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Reactivate a user.",
        "description": "Reactivates a deactivated user, restoring its API keys. Requires an administrator as well as the scope. Recorded as a user_reactivated audit event naming the calling administrator as the principal and the calling API key in the details. Requires the `users:write` scope.",
        "operationId": "reactivateUser",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The user was reactivated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is already active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/role": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Set a user's role.",
        "description": "Sets the role to user or admin. The last active administrator cannot be demoted. Requires an administrator as well as the scope. Recorded as a user_role_changed audit event naming the calling administrator as the principal and the calling API key in the details. Requires the `users:write` scope.",
        "operationId": "setUserRole",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetUserRoleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The role was set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The role is missing or is not user or admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user is the last active administrator, or, with MongoDB storage, another change to the administrators is in progress (reason admin_change_in_progress; Retry-After gives the seconds to wait).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Present only with reason admin_change_in_progress.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/session-keys": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Revoke all of a user's session keys.",
        "description": "Revokes every session key the user holds, signing the person out everywhere. Long-lived keys are unaffected. Requires an administrator as well as the scope. Each key is recorded as an api_key_deleted audit event naming the calling administrator and API key. Requires the `api-keys:write` scope.",
        "operationId": "revokeUserSessionKeys",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "How many session keys were revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevokedSessionKeysResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold api-keys:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/users/{username}/sign-in/unlock": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Clear a user's sign-in lock.",
        "description": "Clears the lock on a user's password sign-in after repeated failed sign-ins, before SIGN_IN_LOCKOUT_MINUTES ends, and resets the count of failures. The lock returns if sign-in keeps failing, so restrict who can reach sign-in when someone is locking a user out deliberately. Requires an administrator as well as the scope. Recorded as a sign_in_unlocked audit event naming the calling administrator and API key. Requires the `users:write` scope.",
        "operationId": "unlockSignIn",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The user's sign-in was unlocked.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold users:write, or the caller is not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "There is no user with that username.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "The user's sign-in is not locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/webhook": {
      "delete": {
        "tags": [
          "Webhook"
        ],
        "summary": "Remove the webhook.",
        "description": "Removes the webhook URL and secret, so asynchronous results are no longer delivered. Admins may remove another user's webhook via the owner parameter. Recorded as a webhook_removed audit event naming the calling user and API key. Requires the `webhooks:write` scope.",
        "operationId": "removeWebhook",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The webhook was removed, or none was set.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold webhooks:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Webhook"
        ],
        "summary": "Get the webhook.",
        "description": "Returns the webhook URL and whether a secret is set; the secret is never returned. Admins may read another user's webhook via the owner parameter. Requires the `webhooks:read` scope.",
        "operationId": "getWebhook",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook configuration. The url is null when none is set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold webhooks:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Webhook"
        ],
        "summary": "Set the webhook.",
        "description": "Sets the webhook URL and secret. The URL must be http or https and its host must be permitted by the administrator's webhook destination allowlist, which refuses private and loopback addresses when empty. The secret must be at least 16 characters. Leave the secret out to keep the one already set, so the URL can change without rotating it. Admins may set another user's webhook via the owner parameter. Recorded as a webhook_configured audit event naming the calling user and API key; the URL and secret are not recorded. Requires the `webhooks:write` scope.",
        "operationId": "setWebhook",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook was set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The URL is missing, the secret is missing and none is set yet, the URL is invalid or not permitted, or the secret is too short. The message says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold webhooks:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/webhook/deliveries": {
      "get": {
        "tags": [
          "Webhook"
        ],
        "summary": "List the webhook's deliveries.",
        "description": "Lists the user's webhook deliveries, newest first, with each one's status, attempts, last error, and when it was queued, last changed, is next due, and was delivered, so a failing webhook can be diagnosed. The secret and the event body are not included. A delivery that ended, delivered or failed, is kept for WEBHOOK_DELIVERIES_TTL_SECONDS (30 days by default). Paged with offset and limit (25 by default, at most 100). Admins may list another user's deliveries via the owner parameter. Requires the `webhooks:read` scope.",
        "operationId": "getDeliveries",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deliveries and the total.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveriesResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold webhooks:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    },
    "/api/webhook/test": {
      "post": {
        "tags": [
          "Webhook"
        ],
        "summary": "Send a test event to the webhook.",
        "description": "Sends one signed WEBHOOK_TEST event to the webhook now, signed and checked against the destination allowlist as a real delivery is, and returns what happened: whether the receiver answered 2xx, its status code, and any error. The event is not queued or retried and does not appear in GET /api/webhook/deliveries. Its body has \"test\": true so a receiver can ignore it. Waits for the receiver up to WEBHOOK_CONNECT_TIMEOUT_SECONDS and WEBHOOK_RESPONSE_TIMEOUT_SECONDS. Admins may test another user's webhook via the owner parameter. Recorded as a webhook_tested audit event with the outcome; the URL and secret are not recorded. Requires the `webhooks:write` scope.",
        "operationId": "testWebhook",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The test was attempted. delivered says whether the receiver accepted it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "401": {
            "description": "The Authorization header is absent or the API key is not recognized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "403": {
            "description": "The key does not hold webhooks:write.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "404": {
            "description": "The owner does not exist, or the caller may not reach it.",
            "content": {},
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "406": {
            "description": "No representation matches Accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "409": {
            "description": "No webhook is set, so there is nothing to test.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "413": {
            "description": "Content Too Large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiKeyResponse": {
        "type": "object",
        "properties": {
          "bootstrap": {
            "type": "boolean"
          },
          "clientAddress": {
            "description": "Session keys only: the address of the client that signed in, so a person can tell their sessions apart. Null for a long-lived key.",
            "type": "string"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "current": {
            "description": "Whether this is the key making the request.",
            "type": "boolean"
          },
          "expiresAt": {
            "description": "Session keys only: when the key's maximum lifetime ends. Null for a long-lived key.",
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "type": "string"
          },
          "idleExpiresAt": {
            "description": "Session keys only: when the key expires unless it is used first. Null for a long-lived key.",
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "description": "Session keys only: when the key was last used. Null for a long-lived key.",
            "type": "string",
            "format": "date-time"
          },
          "prefix": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "session": {
            "description": "Whether this is a session key, issued when a person signed in, rather than a long-lived key.",
            "type": "boolean"
          },
          "userAgent": {
            "description": "Session keys only: the user agent of the client that signed in, such as a browser. Null for a long-lived key, or when the client sent none.",
            "type": "string"
          }
        }
      },
      "ApiKeyScopeDescription": {
        "type": "object",
        "properties": {
          "description": {
            "description": "What the scope allows, including any requirement for an administrator.",
            "type": "string"
          },
          "name": {
            "description": "The scope as an API key carries it, for example policies:read.",
            "type": "string"
          }
        }
      },
      "ApiKeyScopesResponse": {
        "type": "object",
        "properties": {
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKeyScopeDescription"
            }
          }
        }
      },
      "AuditEventView": {
        "properties": {
          "apiKeyId": {
            "type": "string"
          },
          "associatedObject": {
            "type": "string"
          },
          "clientIpAddress": {
            "type": "string"
          },
          "details": {
            "type": "string"
          },
          "event": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "username": {
            "description": "The username of the user who performed the action, when the actor is a user, including a user since deactivated. null for an event with no user actor, such as one Philter recorded itself.",
            "type": "string"
          }
        }
      },
      "AuditExportLimits": {
        "type": "object",
        "properties": {
          "defaultPageSize": {
            "description": "The rows a page of the export has when limit is not given.",
            "type": "integer",
            "format": "int32"
          },
          "maxPageSize": {
            "description": "The most rows a page of the export can have.",
            "type": "integer",
            "format": "int32"
          },
          "maxWindowDays": {
            "description": "The most days between an export's from and to dates.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "CallerCapabilities": {
        "type": "object",
        "properties": {
          "contextCount": {
            "description": "How many contexts the caller has, to compare with contexts.maxPerUser.",
            "type": "integer",
            "format": "int32"
          },
          "crossUserAccess": {
            "description": "Whether the caller may reach other users' resources with owner and all_users: an administrator, with ADMIN_CROSS_USER_ACCESS_ENABLED on. Always false for a user who is not an administrator.",
            "type": "boolean"
          },
          "ledgerDeletion": {
            "description": "Whether the caller may delete and purge ledger chains: an administrator, with LEDGER_DELETION_ENABLED on. Always false for a user who is not an administrator.",
            "type": "boolean"
          },
          "role": {
            "description": "The caller's role, read when the request is made.",
            "type": "string"
          }
        }
      },
      "ChangePasswordRequest": {
        "type": "object",
        "properties": {
          "currentPassword": {
            "description": "The user's current password.",
            "type": "string"
          },
          "newPassword": {
            "description": "At least 16 characters and at most 72 bytes in UTF-8, different from the current password.",
            "type": "string"
          }
        }
      },
      "ContextConflictResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the context was not created: context_exists (the caller already has a context with this name) or context_limit_reached (the caller already has the most contexts a user may have).",
            "type": "string",
            "enum": [
              "context_exists",
              "context_limit_reached"
            ]
          }
        }
      },
      "ContextEntriesExport": {
        "properties": {
          "version": {
            "type": "integer",
            "format": "int32"
          },
          "context": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContextEntryExport"
            }
          }
        }
      },
      "ContextEntryExport": {
        "properties": {
          "filterType": {
            "type": "string"
          },
          "replacement": {
            "type": "string"
          },
          "replacementUuid": {
            "type": "boolean"
          },
          "tokenHash": {
            "type": "string"
          }
        }
      },
      "ContextEntryView": {
        "properties": {
          "filterType": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "reads": {
            "type": "integer",
            "format": "int64"
          },
          "replacement": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContextRules": {
        "type": "object",
        "properties": {
          "maxEntries": {
            "description": "The most entries a context holds. Adding one more evicts the least-read entry. Set by MAX_CONTEXT_SIZE.",
            "type": "integer",
            "format": "int32"
          },
          "maxPerUser": {
            "description": "The most contexts a user can have.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "ContextSummary": {
        "properties": {
          "name": {
            "type": "string"
          },
          "createdAt": {
            "description": "When the context was created.",
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "description": "The username of the user the context belongs to. Present only in an all_users listing.",
            "type": "string"
          }
        }
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "properties": {
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateContextRequest": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "description": "The context's name.",
            "type": "string"
          },
          "disambiguationScope": {
            "description": "Whether span disambiguation learns within each document (document) or across every document redacted in the context (context). Applies when entityTypeDisambiguation is on.",
            "type": "string",
            "default": "document",
            "enum": [
              "document",
              "context"
            ]
          },
          "entityTypeDisambiguation": {
            "description": "Whether to apply entity-type span disambiguation.",
            "type": "boolean",
            "default": false
          },
          "ledger": {
            "description": "Whether to record redactions in the ledger.",
            "type": "boolean",
            "default": false
          }
        }
      },
      "CreateUserRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "description": "Optional. At least 16 characters and at most 72 bytes in UTF-8. The user must change it at next sign-in. Without one, the user can only use API keys.",
            "type": "string"
          },
          "role": {
            "description": "user (the default) or admin.",
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "CreatedApiKeyResponse": {
        "type": "object",
        "properties": {
          "apiKey": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "username": {
            "type": "string"
          }
        }
      },
      "CreatedUserResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "CurrentUserResponse": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "deactivatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "description": "The user's id, which audit events and other records refer to the user by.",
            "type": "string"
          },
          "mfaAvailable": {
            "description": "Whether this deployment lets users enroll in MFA.",
            "type": "boolean"
          },
          "mfaEnabled": {
            "description": "Whether the user is enrolled in TOTP multi-factor authentication. The secret is never returned.",
            "type": "boolean"
          },
          "mfaLocked": {
            "description": "Whether the user's MFA is locked after repeated bad codes, until an administrator unlocks it.",
            "type": "boolean"
          },
          "mfaRequired": {
            "description": "Whether this deployment makes every user who signs in enroll in MFA. Never true while mfaAvailable is false.",
            "type": "boolean"
          },
          "passwordChangeRequired": {
            "description": "Whether the user must change their password at next sign-in, because an administrator set it.",
            "type": "boolean"
          },
          "passwordSet": {
            "description": "Whether the user has a password. A user without one can only use API keys.",
            "type": "boolean"
          },
          "role": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "CustomListConflictResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the list was not created: list_exists (the owner already has a list with this name).",
            "type": "string",
            "enum": [
              "list_exists"
            ]
          }
        }
      },
      "CustomListRequest": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "description": {
            "description": "The list's description. On create, left out means an empty string. On replace, left out keeps the list's description, and an empty string clears it.",
            "type": "string"
          },
          "items": {
            "description": "The list's items. Required, with at least one item that is not empty.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CustomListResponse": {
        "type": "object",
        "properties": {
          "description": {
            "description": "The list's description, or an empty string if it has none.",
            "type": "string"
          },
          "items": {
            "description": "The list's items.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CustomListRules": {
        "type": "object",
        "properties": {
          "descriptionMaxLength": {
            "description": "The most characters a custom list description can have.",
            "type": "integer",
            "format": "int32"
          },
          "itemMaxLength": {
            "description": "The most characters an item can have.",
            "type": "integer",
            "format": "int32"
          },
          "maxItems": {
            "description": "The most items a custom list can have.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "DeletedPolicySummary": {
        "required": [
          "latestRevision",
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "description": "The policy's name. Read its history with GET /api/policies/{policyName}/versions.",
            "type": "string"
          },
          "deletedAt": {
            "description": "When the policy was deleted, or null for a policy deleted before Philter recorded deletions.",
            "type": "string",
            "format": "date-time"
          },
          "deletedBy": {
            "description": "The username of the user who deleted it, or null when that is not known.",
            "type": "string"
          },
          "latestRevision": {
            "description": "The most recent retained revision.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "DocumentSummary": {
        "properties": {
          "type": {
            "description": "What the job produces. redaction, a redacted document, is the only type.",
            "type": "string"
          },
          "documentId": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "policyContentHash": {
            "type": "string"
          },
          "policyName": {
            "type": "string"
          },
          "policyVersion": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExplanationSpan": {
        "type": "object",
        "properties": {
          "applied": {
            "type": "boolean"
          },
          "characterEnd": {
            "type": "integer",
            "format": "int32"
          },
          "characterStart": {
            "type": "integer",
            "format": "int32"
          },
          "classification": {
            "type": "string"
          },
          "confidence": {
            "type": "number"
          },
          "context": {
            "type": "string"
          },
          "filterType": {
            "example": "SSN",
            "type": "string"
          },
          "ignored": {
            "type": "boolean"
          },
          "lineNumber": {
            "type": "integer",
            "format": "int32"
          },
          "lowerLeftX": {
            "type": "number"
          },
          "lowerLeftY": {
            "type": "number"
          },
          "pageNumber": {
            "type": "integer",
            "format": "int32"
          },
          "paragraphNumber": {
            "type": "integer",
            "format": "int32"
          },
          "priority": {
            "type": "integer",
            "format": "int32"
          },
          "replacement": {
            "type": "string"
          },
          "salt": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "upperRightX": {
            "type": "number"
          },
          "upperRightY": {
            "type": "number"
          }
        }
      },
      "GenericResponse": {
        "type": "object",
        "properties": {
          "field": {
            "description": "For a request refused as invalid, the parameter or body field that was invalid, when one is.",
            "type": "string"
          },
          "message": {
            "description": "What happened, written for a person to read. It may change; use reason in code.",
            "type": "string"
          },
          "reason": {
            "description": "For an error, why the request was refused, as a stable code a client can act on. The codes are listed in the API documentation under Errors.",
            "type": "string"
          }
        }
      },
      "GetApiKeysResponse": {
        "type": "object",
        "properties": {
          "apiKeys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKeyResponse"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetAuditResponse": {
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEventView"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetContextEntriesResponse": {
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContextEntryView"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetContextResponse": {
        "properties": {
          "disambiguationScope": {
            "type": "string",
            "enum": [
              "document",
              "context"
            ]
          },
          "entityTypeDisambiguation": {
            "type": "boolean"
          },
          "filterTypes": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            }
          },
          "ledger": {
            "type": "boolean"
          },
          "size": {
            "type": "integer",
            "format": "int64"
          },
          "untyped": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetContextsResponse": {
        "properties": {
          "contexts": {
            "description": "The contexts on this page.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContextSummary"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetCustomListsResponse": {
        "properties": {
          "lists": {
            "description": "The lists on this page, each with its name, description, and number of items.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListSummaryResponse"
            }
          },
          "total": {
            "description": "How many lists the listing has across every page.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetDocumentsResponse": {
        "properties": {
          "documents": {
            "description": "The documents on this page.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentSummary"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetHoldsResponse": {
        "type": "object",
        "properties": {
          "holds": {
            "description": "The holds on this page. With all_users, each also names its owner.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OwnedLegalHoldResponse"
            }
          },
          "total": {
            "description": "How many holds the listing has across every page.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetLedgerResponse": {
        "properties": {
          "chains": {
            "description": "The chain heads on this page. With all_users, each also names its owner.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OwnedLedgerEntryView"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetModelsResponse": {
        "type": "object",
        "properties": {
          "models": {
            "description": "Every model, ordered by name. Empty when the deployment has none.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModelResponse"
            }
          }
        }
      },
      "GetPiiCountsResponse": {
        "type": "object",
        "properties": {
          "counts": {
            "description": "The page of counts, one per context and day, most recent day first, then by context.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PiiCountView"
            }
          },
          "recordingEnabled": {
            "description": "Whether Philter is recording PII counts now (the diffuseCountsEnabled setting). When it is false, no new counts are being recorded, so an empty listing does not mean no PII was found.",
            "type": "boolean"
          },
          "total": {
            "description": "How many context and day aggregates match the filters.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetPoliciesResponse": {
        "type": "object",
        "properties": {
          "policies": {
            "description": "The policies on this page. With deleted, each describes a deleted policy.",
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/PolicySummary"
                },
                {
                  "$ref": "#/components/schemas/DeletedPolicySummary"
                }
              ]
            }
          },
          "total": {
            "description": "How many policies the listing has across every page.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GetPolicyVersionsResponse": {
        "type": "object",
        "properties": {
          "total": {
            "description": "How many versions the policy has retained.",
            "type": "integer",
            "format": "int64"
          },
          "versions": {
            "description": "The versions on this page.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PolicyVersionSummary"
            }
          }
        }
      },
      "GetRedactionStatusResponse": {
        "properties": {
          "type": {
            "type": "string"
          },
          "documentId": {
            "type": "string"
          },
          "effectiveConfigurationHash": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "policyContentHash": {
            "type": "string"
          },
          "policyName": {
            "type": "string"
          },
          "policyVersion": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "GetUsersResponse": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "format": "int64"
          },
          "users": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserResponse"
            }
          }
        }
      },
      "ImportContextEntriesResponse": {
        "properties": {
          "inserted": {
            "type": "integer",
            "format": "int32"
          },
          "overwritten": {
            "type": "integer",
            "format": "int32"
          },
          "skipped": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "LedgerChainResponse": {
        "properties": {
          "documentId": {
            "type": "string"
          },
          "entries": {
            "description": "The entries on this page, in chain order. Left out by the validity endpoint and for a chain that could not be validated.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerEntryView"
            }
          },
          "hashChainValid": {
            "type": "boolean"
          },
          "signaturesValid": {
            "type": "boolean"
          },
          "signedEntries": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "description": "How many entries the whole chain has, across every page. Left out with entries.",
            "type": "integer",
            "format": "int64"
          },
          "unsignedEntries": {
            "type": "integer",
            "format": "int32"
          },
          "valid": {
            "type": "boolean"
          },
          "validationError": {
            "description": "Why the chain could not be validated. Present only then, with valid false and the hash, signature, and entry-count fields left out.",
            "type": "string"
          }
        }
      },
      "LedgerEntryView": {
        "properties": {
          "type": {
            "type": "string"
          },
          "documentHash": {
            "type": "string"
          },
          "documentId": {
            "type": "string"
          },
          "effectiveHash": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "hash": {
            "type": "string"
          },
          "policyContentHash": {
            "type": "string"
          },
          "policyName": {
            "type": "string"
          },
          "policyVersion": {
            "type": "integer",
            "format": "int32"
          },
          "previousHash": {
            "type": "string"
          },
          "readError": {
            "description": "Present only when the entry could not be read, for example because it no longer decrypts. The fields stored in the clear are still given; replacement is not.",
            "type": "string"
          },
          "replacement": {
            "type": "string"
          },
          "signature": {
            "type": "string"
          },
          "signingKeyId": {
            "type": "string"
          },
          "startPosition": {
            "type": "integer",
            "format": "int64"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "token": {
            "type": "string"
          }
        }
      },
      "LedgerExport": {
        "properties": {
          "version": {
            "type": "integer",
            "format": "int32"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "documentId": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerEntryView"
            }
          },
          "signingKeys": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "LedgerRefusalResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the request was refused: entry_unreadable (an entry in the chain could not be read).",
            "type": "string",
            "enum": [
              "entry_unreadable"
            ]
          }
        }
      },
      "LegalHoldConflictResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the hold was not changed: hold_exists (the owner already has a hold with this reference) or operation_in_progress (another evidence or hold operation for the owner is active or requires recovery).",
            "type": "string",
            "enum": [
              "hold_exists",
              "operation_in_progress"
            ]
          }
        }
      },
      "LegalHoldRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "scopeType": {
            "type": "string"
          },
          "scopeValue": {
            "description": "For document_chain, the document id (required). For user, optional: the hold covers all of its owner's evidence, and if given this must be the owner's username, which is stored either way.",
            "type": "string"
          }
        }
      },
      "LegalHoldResponse": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "scopeType": {
            "type": "string"
          },
          "scopeValue": {
            "type": "string"
          },
          "setAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LegalHoldRules": {
        "type": "object",
        "properties": {
          "scopeTypes": {
            "description": "The scope types a legal hold can have.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "LimitsResponse": {
        "type": "object",
        "properties": {
          "auditExport": {
            "description": "Audit log export limits.",
            "$ref": "#/components/schemas/AuditExportLimits"
          },
          "caller": {
            "description": "What the caller may do.",
            "$ref": "#/components/schemas/CallerCapabilities"
          },
          "contexts": {
            "description": "Context limits.",
            "$ref": "#/components/schemas/ContextRules"
          },
          "customLists": {
            "description": "Custom list sizes.",
            "$ref": "#/components/schemas/CustomListRules"
          },
          "legalHolds": {
            "description": "Legal hold rules.",
            "$ref": "#/components/schemas/LegalHoldRules"
          },
          "names": {
            "description": "The rule for names used in a request path: custom list names, context names, legal hold references, and usernames.",
            "$ref": "#/components/schemas/PathSafeNameRules"
          },
          "password": {
            "description": "The rules a password must meet.",
            "$ref": "#/components/schemas/PasswordRules"
          },
          "policies": {
            "description": "Policy names, descriptions, and notes.",
            "$ref": "#/components/schemas/PolicyRules"
          },
          "redactLists": {
            "description": "Always-redact and never-redact list sizes.",
            "$ref": "#/components/schemas/RedactListRules"
          },
          "requests": {
            "description": "Request sizes and paging.",
            "$ref": "#/components/schemas/RequestLimits"
          },
          "sessionKeys": {
            "description": "How long a session key, the key issued by password sign-in, stays valid.",
            "$ref": "#/components/schemas/SessionKeyLimits"
          },
          "users": {
            "description": "User rules.",
            "$ref": "#/components/schemas/UserRules"
          },
          "webhook": {
            "description": "Webhook rules.",
            "$ref": "#/components/schemas/WebhookRules"
          }
        }
      },
      "ListSummaryResponse": {
        "description": "The lists on this page, each with its name, description, and number of items.",
        "properties": {
          "description": {
            "description": "The list's description, or an empty string if it has none.",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owner": {
            "description": "The username of the user the list belongs to. Present only in an all_users listing.",
            "type": "string"
          },
          "size": {
            "description": "How many items the list holds.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MfaCodeRequest": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          }
        }
      },
      "MfaEnrollmentResponse": {
        "type": "object",
        "properties": {
          "otpauthUri": {
            "description": "The otpauth:// URI to show as a QR code for an authenticator app to scan.",
            "type": "string"
          },
          "secret": {
            "description": "The setup key, Base32, for entering by hand. Returned only here.",
            "type": "string"
          }
        }
      },
      "ModelResponse": {
        "type": "object",
        "properties": {
          "name": {
            "description": "The model's name, which a policy gives as a ph-eye filter's phEyeConfiguration.modelPath.",
            "type": "string"
          },
          "encoder": {
            "description": "The encoder the model was trained from, from its gliner_config.json, for example microsoft/deberta-v3-small. Absent when the configuration does not say.",
            "type": [
              "string",
              "null"
            ]
          },
          "maxLength": {
            "description": "The most words of a text the model reads; later words are not examined. Absent when the configuration does not say.",
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "maxWidth": {
            "description": "The most words one detected entity can span. Absent when the configuration does not say.",
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "sizeBytes": {
            "description": "The combined size, in bytes, of its gliner_config.json, tokenizer.json, and model.onnx.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "OwnedLedgerEntryView": {
        "properties": {
          "type": {
            "type": "string"
          },
          "documentHash": {
            "type": "string"
          },
          "documentId": {
            "type": "string"
          },
          "effectiveHash": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "hash": {
            "type": "string"
          },
          "owner": {
            "description": "The username of the user the chain belongs to. Present only in an all_users listing.",
            "type": "string"
          },
          "policyContentHash": {
            "type": "string"
          },
          "policyName": {
            "type": "string"
          },
          "policyVersion": {
            "type": "integer",
            "format": "int32"
          },
          "previousHash": {
            "type": "string"
          },
          "readError": {
            "description": "Present only when the entry could not be read, for example because it no longer decrypts. The fields stored in the clear are still given; replacement is not.",
            "type": "string"
          },
          "replacement": {
            "type": "string"
          },
          "signature": {
            "type": "string"
          },
          "signingKeyId": {
            "type": "string"
          },
          "startPosition": {
            "type": "integer",
            "format": "int64"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "token": {
            "type": "string"
          }
        }
      },
      "OwnedLegalHoldResponse": {
        "type": "object",
        "properties": {
          "owner": {
            "description": "The username of the user the hold belongs to. Present only in an all_users listing.",
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "scopeType": {
            "type": "string"
          },
          "scopeValue": {
            "type": "string"
          },
          "setAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PasswordRules": {
        "type": "object",
        "properties": {
          "maxBytes": {
            "description": "The most bytes a password can have in UTF-8.",
            "type": "integer",
            "format": "int32"
          },
          "minCharacters": {
            "description": "The fewest characters a password can have.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PathSafeNameRules": {
        "type": "object",
        "properties": {
          "controlCharactersForbidden": {
            "description": "Whether control characters are refused too.",
            "type": "boolean"
          },
          "forbiddenCharacters": {
            "description": "The characters such a name cannot contain.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "reservedNames": {
            "description": "Whole names that are refused.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rule": {
            "description": "The rule in words, as Philter's error messages state it.",
            "type": "string"
          }
        }
      },
      "PiiCountView": {
        "type": "object",
        "properties": {
          "context": {
            "description": "The context the redactions ran in. Given for the caller's own counts, and for other users' only when ADMIN_CROSS_USER_ACCESS_ENABLED is true; otherwise absent.",
            "type": "string"
          },
          "counts": {
            "description": "For each PII type, how many redactions that day found at least one of it.",
            "example": {
              "email-address": 8900,
              "ssn": 1240
            },
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            }
          },
          "day": {
            "description": "The UTC day the redactions ran on, as YYYY-MM-DD.",
            "example": "2026-10-01",
            "type": "string"
          },
          "id": {
            "description": "Identifies this user's context and day, the same on every read, so a client paging through the counts can tell buckets apart without their names.",
            "type": "string"
          },
          "owner": {
            "description": "The username of the user whose redactions these are. Given for the caller's own counts, and for other users' only when ADMIN_CROSS_USER_ACCESS_ENABLED is true; otherwise absent.",
            "type": "string"
          },
          "totalDocuments": {
            "description": "How many redactions that day found any PII.",
            "type": "integer",
            "format": "int64"
          },
          "updatedAt": {
            "description": "When a redaction was last counted.",
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PolicyConflictResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the policy was not changed: policy_managed (it is a built-in managed policy), policy_changed (it changed since the read If-Match names, or concurrently; read it again and retry), policy_exists (a policy with this name was created concurrently), or policy_default (the default policy cannot be deleted).",
            "type": "string",
            "enum": [
              "policy_managed",
              "policy_changed",
              "policy_exists",
              "policy_default"
            ]
          }
        }
      },
      "PolicyDetailsRequest": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "PolicyDetailsResponse": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "etag": {
            "description": "Identifies the policy together with its description and notes, and changes when any of them does; the revision alone does not change when only the details do. The ETag header carries the same value, quoted. Send it in If-Match to make a replace, a details change, or a rollback conditional.",
            "example": "7-0123456789abcdef",
            "type": "string"
          },
          "lastUpdated": {
            "type": "string",
            "format": "date-time"
          },
          "managed": {
            "type": "boolean"
          },
          "notes": {
            "type": "string"
          },
          "revision": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PolicyRollbackResponse": {
        "type": "object",
        "properties": {
          "revision": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PolicyRules": {
        "type": "object",
        "properties": {
          "defaultPolicyName": {
            "description": "The policy every user is given, which cannot be deleted.",
            "type": "string"
          },
          "descriptionMaxLength": {
            "description": "The most characters a policy description can have.",
            "type": "integer",
            "format": "int32"
          },
          "nameMaxLength": {
            "description": "The most characters a policy name can have.",
            "type": "integer",
            "format": "int32"
          },
          "namePattern": {
            "description": "The regular expression a policy name must match.",
            "type": "string"
          },
          "notesMaxLength": {
            "description": "The most characters a policy's notes can have.",
            "type": "integer",
            "format": "int32"
          },
          "reservedNamePrefix": {
            "description": "The prefix of managed policies' names, which a user's policy cannot start with.",
            "type": "string"
          },
          "reservedNames": {
            "description": "Whole names a policy cannot have, because the API's paths use them for something else.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PolicySummary": {
        "required": [
          "name",
          "revision"
        ],
        "type": "object",
        "properties": {
          "description": {
            "description": "The policy's description, or an empty string if it has none.",
            "type": "string"
          },
          "name": {
            "description": "The policy's name. A managed policy's begins with managed_.",
            "type": "string"
          },
          "createdAt": {
            "description": "When the policy was created.",
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "description": "The username of the user the policy belongs to. Present only in an all_users listing.",
            "type": "string"
          },
          "revision": {
            "description": "The policy's current revision.",
            "type": "integer",
            "format": "int32"
          },
          "updatedAt": {
            "description": "When the policy was last changed.",
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PolicyVersionSummary": {
        "type": "object",
        "properties": {
          "author": {
            "description": "The username of the user whose change produced this revision, or null when it is not known: for a revision made before Philter recorded authors, for a managed policy, or for a revision first captured when the policy was used to redact.",
            "type": "string"
          },
          "capturedTimestamp": {
            "type": "string",
            "format": "date-time"
          },
          "contentHash": {
            "type": "string"
          },
          "revision": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "RedactListConflictResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the list was not written: redact_list_changed (the list is no longer at the revision given in If-Match).",
            "type": "string",
            "enum": [
              "redact_list_changed"
            ]
          }
        }
      },
      "RedactListRequest": {
        "properties": {
          "terms": {
            "description": "The list's complete contents. An empty array or an omitted field clears the list.",
            "type": "array",
            "items": {
              "description": "The list's complete contents. An empty array or an omitted field clears the list.",
              "type": "string"
            }
          }
        }
      },
      "RedactListResponse": {
        "type": "object",
        "properties": {
          "revision": {
            "description": "The list's revision, which every write of the list increments. Send it back in If-Match when replacing the list so the write is refused if someone changed the list since. A list never written is at 0. The ETag header carries the same value.",
            "type": "integer",
            "format": "int64"
          },
          "terms": {
            "description": "The list's terms. A list with no saved terms is an empty array.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RedactListRules": {
        "type": "object",
        "properties": {
          "maxTerms": {
            "description": "The most terms each of the always-redact and never-redact lists can have.",
            "type": "integer",
            "format": "int32"
          },
          "termMaxLength": {
            "description": "The most characters a term can have.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "RedactListsRequest": {
        "properties": {
          "alwaysRedact": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "neverRedact": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RedactListsResponse": {
        "properties": {
          "alwaysRedact": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "alwaysRedactRevision": {
            "description": "The always-redact list's revision, for If-Match on PUT /api/redact-lists/always.",
            "type": "integer",
            "format": "int64"
          },
          "neverRedact": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "neverRedactRevision": {
            "description": "The never-redact list's revision, for If-Match on PUT /api/redact-lists/never.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "ReidentifyRequest": {
        "type": "object",
        "properties": {
          "policyName": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "strategy": {
            "type": "string"
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ReidentifyResponse": {
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReidentifyResult"
            }
          }
        }
      },
      "ReidentifyResult": {
        "properties": {
          "decrypted": {
            "type": "string"
          },
          "encrypted": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "ReidentifyTextRequest": {
        "type": "object",
        "properties": {
          "policyName": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "ReidentifyTextResponse": {
        "properties": {
          "failed": {
            "type": "integer",
            "format": "int32"
          },
          "found": {
            "type": "integer",
            "format": "int32"
          },
          "restored": {
            "type": "integer",
            "format": "int32"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "RequestLimits": {
        "type": "object",
        "properties": {
          "defaultPageSize": {
            "description": "The page size a listing uses when limit is not given.",
            "type": "integer",
            "format": "int32"
          },
          "maxBodyBytes": {
            "description": "The largest body any other request accepts, in bytes. Set by MAX_FILE_SIZE_BYTES_OTHER.",
            "type": "integer",
            "format": "int64"
          },
          "maxDocumentBytes": {
            "description": "The largest body POST /api/filter and POST /api/explain accept, in bytes. Set by MAX_FILE_SIZE_BYTES.",
            "type": "integer",
            "format": "int64"
          },
          "maxPageSize": {
            "description": "The largest limit a listing honors. A larger limit is reduced to this.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "RevokedSessionKeysResponse": {
        "type": "object",
        "properties": {
          "revoked": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "SessionKeyLimits": {
        "type": "object",
        "properties": {
          "idleTimeoutMinutes": {
            "description": "Minutes without a request after which a session key expires. Set by SESSION_KEY_IDLE_TIMEOUT_MINUTES.",
            "type": "integer",
            "format": "int32"
          },
          "maxLifetimeMinutes": {
            "description": "Minutes after issue at which a session key expires, whatever its use. Set by SESSION_KEY_MAX_LIFETIME_MINUTES.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "SetApiKeyScopesRequest": {
        "type": "object",
        "properties": {
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SetPasswordRequest": {
        "type": "object",
        "properties": {
          "password": {
            "description": "At least 16 characters and at most 72 bytes in UTF-8.",
            "type": "string"
          }
        }
      },
      "SetUserEmailRequest": {
        "type": "object",
        "properties": {
          "email": {
            "description": "The new email address. null or empty removes the user's email address.",
            "type": "string"
          }
        }
      },
      "SetUserRoleRequest": {
        "type": "object",
        "properties": {
          "role": {
            "description": "user or admin.",
            "type": "string"
          }
        }
      },
      "SetWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "secret": {
            "type": "string"
          }
        }
      },
      "SettingsResponse": {
        "type": "object",
        "properties": {
          "crossUserAccessEnabled": {
            "description": "Whether administrators may use owner and all_users to reach other users' resources. Set by ADMIN_CROSS_USER_ACCESS_ENABLED; read-only, and ignored by PATCH.",
            "type": "boolean",
            "readOnly": true
          },
          "diffuseCountsEnabled": {
            "type": "boolean"
          },
          "ledgerDeletionEnabled": {
            "description": "Whether administrators may delete and purge ledger chains. Set by LEDGER_DELETION_ENABLED; read-only, and ignored by PATCH.",
            "type": "boolean",
            "readOnly": true
          },
          "mfaAvailable": {
            "type": "boolean"
          },
          "mfaRequired": {
            "type": "boolean"
          },
          "phieldApiKeySet": {
            "type": "boolean"
          },
          "phieldEnabled": {
            "type": "boolean"
          },
          "phieldOrganization": {
            "type": "string"
          },
          "phieldSourceId": {
            "type": "string"
          },
          "phieldUrl": {
            "type": "string"
          },
          "signingEnabled": {
            "type": "boolean"
          },
          "signingKeyExternallyManaged": {
            "description": "Whether the output signing key comes from PHILTER_SIGNING_KEY_PATH, in which case it cannot be rotated through the API. Read-only, and ignored by PATCH.",
            "type": "boolean",
            "readOnly": true
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "webhookAllowlist": {
            "type": "string"
          }
        }
      },
      "SignInChallengeResponse": {
        "required": [
          "challenge"
        ],
        "type": "object",
        "properties": {
          "challenge": {
            "description": "Single-use. Any attempt with it, right or wrong, uses it up.",
            "type": "string"
          },
          "challengeExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "mfaRequired": {
            "description": "Always true: send the challenge and a code to POST /api/sign-in/mfa.",
            "type": "boolean"
          }
        }
      },
      "SignInMfaRequest": {
        "type": "object",
        "properties": {
          "challenge": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        }
      },
      "SignInOptionsResponse": {
        "type": "object",
        "properties": {
          "password": {
            "description": "The rules a password must meet, for a page where a person sets one.",
            "$ref": "#/components/schemas/PasswordRules"
          }
        }
      },
      "SignInRequest": {
        "type": "object",
        "properties": {
          "password": {
            "description": "The user's password. Never logged or audited.",
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "SignInResponse": {
        "required": [
          "apiKey"
        ],
        "type": "object",
        "properties": {
          "apiKey": {
            "description": "The session key. Send it as a bearer token. It is returned here and nowhere else.",
            "type": "string"
          },
          "expiresAt": {
            "description": "When the key's maximum lifetime ends.",
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "description": "The session key's id, as GET /api/api-keys lists it, so a client can recognize its own key.",
            "type": "string"
          },
          "idleExpiresAt": {
            "description": "When the key expires unless it is used first. Each request moves it forward.",
            "type": "string",
            "format": "date-time"
          },
          "mfaEnrollmentRequired": {
            "description": "Whether the user must enroll in MFA first. If so, the key can only enroll with POST /api/users/me/mfa and POST /api/users/me/mfa/confirm, and sign out.",
            "type": "boolean"
          },
          "passwordChangeRequired": {
            "description": "Whether the password must be changed first. If so, the key can only change the password with PUT /api/users/me/password and sign out.",
            "type": "boolean"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "username": {
            "type": "string"
          }
        }
      },
      "SignInThrottledResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Why the sign-in was refused: locked (the username is locked after repeated failures) or rate_limited (the client address is over the rate limit).",
            "type": "string",
            "enum": [
              "locked",
              "rate_limited"
            ]
          }
        }
      },
      "StartMfaEnrollmentRequest": {
        "type": "object",
        "properties": {
          "password": {
            "type": "string"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "properties": {
          "applicationVersion": {
            "type": "string"
          },
          "gitCommit": {
            "type": "string"
          },
          "redactionPolicySchemaVersion": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "UpdateContextRequest": {
        "type": "object",
        "properties": {
          "disambiguationScope": {
            "description": "Whether span disambiguation learns within each document (document) or across every document redacted in the context (context).",
            "type": "string",
            "enum": [
              "document",
              "context"
            ]
          },
          "entityTypeDisambiguation": {
            "description": "Whether to apply entity-type span disambiguation.",
            "type": "boolean"
          },
          "ledger": {
            "description": "Whether to record redactions in the ledger.",
            "type": "boolean"
          }
        }
      },
      "UpdateSettingsRequest": {
        "type": "object",
        "properties": {
          "diffuseCountsEnabled": {
            "type": "boolean"
          },
          "mfaAvailable": {
            "description": "Whether users may enroll in TOTP multi-factor authentication.",
            "type": "boolean"
          },
          "mfaRequired": {
            "description": "Whether every user who signs in must enroll in MFA. Requires mfaAvailable.",
            "type": "boolean"
          },
          "phieldApiKey": {
            "type": "string"
          },
          "phieldEnabled": {
            "type": "boolean"
          },
          "phieldOrganization": {
            "type": "string"
          },
          "phieldSourceId": {
            "type": "string"
          },
          "phieldUrl": {
            "type": "string"
          },
          "signingEnabled": {
            "type": "boolean"
          },
          "webhookAllowlist": {
            "type": "string"
          }
        }
      },
      "UserResponse": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "deactivatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "description": "The user's id, which audit events and other records refer to the user by.",
            "type": "string"
          },
          "mfaEnabled": {
            "description": "Whether the user is enrolled in TOTP multi-factor authentication. The secret is never returned.",
            "type": "boolean"
          },
          "mfaLocked": {
            "description": "Whether the user's MFA is locked after repeated bad codes, until an administrator unlocks it.",
            "type": "boolean"
          },
          "passwordChangeRequired": {
            "description": "Whether the user must change their password at next sign-in, because an administrator set it.",
            "type": "boolean"
          },
          "passwordSet": {
            "description": "Whether the user has a password. A user without one can only use API keys.",
            "type": "boolean"
          },
          "role": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "UserRules": {
        "type": "object",
        "properties": {
          "reservedUsernames": {
            "description": "Usernames that cannot be used, compared without regard to case.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "roles": {
            "description": "The roles a user can have.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WebhookDeliveriesResponse": {
        "type": "object",
        "properties": {
          "deliveries": {
            "description": "The deliveries on this page, newest first.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryView"
            }
          },
          "total": {
            "description": "How many deliveries the user has.",
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "WebhookDeliveryView": {
        "type": "object",
        "properties": {
          "url": {
            "description": "Where the delivery is sent: the webhook URL when the event was queued.",
            "type": "string"
          },
          "attempts": {
            "description": "How many attempts have been made.",
            "type": "integer",
            "format": "int32"
          },
          "createdAt": {
            "description": "When the event was queued.",
            "type": "string",
            "format": "date-time"
          },
          "deliveredAt": {
            "description": "When the receiver accepted the delivery.",
            "type": "string",
            "format": "date-time"
          },
          "documentId": {
            "description": "The document the event is about.",
            "type": "string"
          },
          "event": {
            "description": "The event: DOCUMENT_REDACTION_COMPLETE or DOCUMENT_REDACTION_FAILED.",
            "type": "string"
          },
          "id": {
            "description": "The delivery's id, sent as X-Philter-Delivery-Id with each attempt.",
            "type": "string"
          },
          "lastError": {
            "description": "Why the most recent attempt failed, or null.",
            "type": "string"
          },
          "nextAttemptAt": {
            "description": "When the next attempt is due, for a delivery that is PENDING.",
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "description": "PENDING (waiting for its next attempt), PROCESSING (being sent), DELIVERED, or FAILED (every attempt failed).",
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSING",
              "DELIVERED",
              "FAILED"
            ]
          },
          "updatedAt": {
            "description": "When the delivery last changed.",
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "secretSet": {
            "type": "boolean"
          }
        }
      },
      "WebhookRules": {
        "type": "object",
        "properties": {
          "secretMinLength": {
            "description": "The fewest characters a webhook secret can have.",
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "WebhookTestResponse": {
        "type": "object",
        "properties": {
          "delivered": {
            "description": "Whether the receiver answered with a 2xx status.",
            "type": "boolean"
          },
          "deliveryId": {
            "description": "The test event's X-Philter-Delivery-Id, to find it in the receiver's logs.",
            "type": "string"
          },
          "durationMillis": {
            "description": "How long the attempt took, in milliseconds.",
            "type": "integer",
            "format": "int64"
          },
          "error": {
            "description": "Why the test was not delivered, or null when it was.",
            "type": "string"
          },
          "statusCode": {
            "description": "The status the receiver answered with, or null when there was no answer, such as when the connection failed, timed out, or the destination is not permitted.",
            "type": "integer",
            "format": "int32"
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "description": "A Philter API key, sent as `Authorization: Bearer <api key>`. The first comes from PHILTER_BOOTSTRAP_API_KEY; create more with POST /api/api-keys.",
        "type": "http",
        "scheme": "bearer"
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "The id Philter gives the request. The audit events the request causes carry it as request_id.",
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "description": "Operations for setting, listing, retrieving, and releasing legal holds. An active hold blocks all deletion and purge of the evidence it covers until the hold is released. Every hold lifecycle event is audited.",
      "name": "Legal Holds"
    },
    {
      "description": "Operations for browsing policy version history, diffing revisions, and rolling back to a prior revision.",
      "name": "Policy Versions"
    },
    {
      "description": "Operations for retrieving asynchronously redacted documents.",
      "name": "Documents"
    },
    {
      "description": "Operations for creating and managing custom lists.",
      "name": "Custom Lists"
    },
    {
      "description": "Redact text and PDF documents.",
      "name": "Filter"
    },
    {
      "description": "Operations for managing an account's always-redact and never-redact term lists.",
      "name": "Always/Never Redact Lists"
    },
    {
      "description": "List, create, re-scope, and revoke API keys. A caller manages its own keys; an administrator can also manage other users' keys.",
      "name": "API Keys"
    },
    {
      "description": "Governed, audited reversal of cryptographically-redacted values.",
      "name": "Re-identification"
    },
    {
      "description": "Operations for creating, retrieving, deleting, and compiling redaction policies.",
      "name": "Policies"
    },
    {
      "description": "Read-only access to the audit log.",
      "name": "Audit Log"
    },
    {
      "description": "Read the PII type counts Philter records for Philter Diffuse. Requires an administrator.",
      "name": "PII Counts"
    },
    {
      "description": "Redact text and return a detailed explanation of what was redacted and why.",
      "name": "Explain"
    },
    {
      "description": "Operations for viewing, exporting, verifying, and deleting redaction-ledger chains.",
      "name": "Redaction Ledger"
    },
    {
      "description": "The limits and rules Philter enforces, and what the caller may do.",
      "name": "Limits"
    },
    {
      "description": "Create and manage users and their passwords. Requires an administrator, except for reading the calling key's own user and changing its own password.",
      "name": "Users"
    },
    {
      "description": "Unauthenticated health, including the supported redaction policy schema version.",
      "name": "Health"
    },
    {
      "description": "Read, set, and remove the user's webhook for asynchronous redaction results.",
      "name": "Webhook"
    },
    {
      "description": "The local PhEye models a policy can name.",
      "name": "Models"
    },
    {
      "description": "Sign in with a username and password to get a session key. Disabled by default.",
      "name": "Sign-in"
    },
    {
      "description": "Operations for creating and managing contexts.",
      "name": "Contexts"
    },
    {
      "description": "Read and change the deployment's admin settings. Requires an administrator.",
      "name": "Settings"
    },
    {
      "description": "Retrieve the public signing key used to verify Philter output signatures, and rotate it.",
      "name": "Signing"
    }
  ]
}