{ "openapi": "3.1.0", "info": { "title": "Philter API", "version": "4.0.0-SNAPSHOT" }, "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. Requires the `api-keys:read` scope.", "operationId": "getApiKeys", "parameters": [ { "name": "Authorization", "in": "header", "required": true, "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 keys.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GetApiKeysResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold api-keys:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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" } } } }, "400": { "description": "No scopes were given, or one of them is not a scope.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold api-keys:write, or a requested scope is not held by the calling key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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." }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "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" } } } }, "404": { "description": "There is no active key with that ID that the caller may manage." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The key is the one making the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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" } } } }, "400": { "description": "No scopes were given, or one of them is not a scope.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold api-keys:write, a requested scope is not held by the calling key, or the key being changed holds a scope the calling key does not.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no active key with that ID that the caller may manage." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": "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" } } } }, "400": { "description": "A timestamp could not be parsed, the range is reversed, or the event type is not one Philter emits.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold audit:read, or the caller is not an administrator.", "content": { "application/json": { "schema": { "type": "string" } } } }, "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": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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, with the same columns as the dashboard export. 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), starting offset events into the range; the X-Philter-Export-Truncated header is true when more remain, and X-Philter-Export-Next-Offset gives the offset to request next. Events are ordered by timestamp and then id, so paging a range that is no longer receiving events returns each exactly once; a range that includes the current day can repeat events across pages. 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", "default": 0 } }, { "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": "object" } } } }, "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, or limit is not a number. The body says which.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold audit:read, or the caller is not an administrator.", "content": { "text/csv": { "schema": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/contexts": { "get": { "tags": [ "Contexts" ], "summary": "Get the names of existing contexts.", "description": "Get the names of the caller's contexts, paged. Admins may list another user's contexts with owner, or every user's with all_users=true, which returns each context's name and 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": "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": "Context names; with all_users, each context's name and owner.", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/GetContextsResponse" }, { "$ref": "#/components/schemas/GetAllUsersContextsResponse" } ] } } } }, "400": { "description": "Both owner and all_users were given.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "post": { "tags": [ "Contexts" ], "summary": "Create a context.", "description": "Create a new context. Requires the `contexts:write` scope.", "operationId": "createContext", "parameters": [ { "name": "Authorization", "in": "header", "required": true, "schema": { "type": "string" } }, { "name": "name", "in": "query", "required": true, "schema": { "type": "string" } }, { "name": "entity_type_disambiguation", "in": "query", "required": false, "schema": { "type": "boolean", "default": false } }, { "name": "ledger", "in": "query", "required": false, "schema": { "type": "boolean", "default": false } }, { "name": "owner", "in": "query", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "The context was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "The context could not be created.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "A context with this name already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": { "200": { "description": "The context was deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "The context could not be deleted.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The context has open asynchronous redaction jobs and cannot be deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "A context with the given name does not exist.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "put": { "tags": [ "Contexts" ], "summary": "Update a context's settings.", "description": "Update the entity_type_disambiguation and ledger flags on an existing context. 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": "entity_type_disambiguation", "in": "query", "required": false, "schema": { "type": "boolean", "default": false } }, { "name": "ledger", "in": "query", "required": false, "schema": { "type": "boolean", "default": false } }, { "name": "owner", "in": "query", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "The context was updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Context not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Not Found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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": "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Context not found.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Context not found.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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" } } } }, "400": { "description": "The payload or on_conflict value is invalid.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Context not found.", "content": { "application/json": { "schema": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Not Found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/documents": { "get": { "tags": [ "Documents" ], "summary": "List documents submitted for async redaction.", "description": "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": "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Owner unavailable or inaccessible." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": { "200": { "description": "The record and any stored redacted bytes were deleted." }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Unknown document id." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "Notification reconciliation is pending.", "content": { "text/plain": { "schema": { "type": "string" } } }, "headers": { "Retry-After": { "schema": { "example": 5, "type": "integer", "format": "int32" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Unknown document id.", "content": { "*/*": { "schema": { "type": "string", "format": "byte" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "Redaction not yet complete.", "content": { "*/*": { "schema": { "type": "string", "format": "byte" } } } }, "410": { "description": "Redaction failed.", "content": { "*/*": { "schema": { "type": "string", "format": "byte" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/documents/{documentId}/status": { "get": { "tags": [ "Documents" ], "summary": "Get the status of an async redaction.", "description": "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Unknown document id.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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", "additionalProperties": true }, "filteredText": { "type": "string" }, "policyContentHash": { "type": "string" }, "policyName": { "type": "string" }, "policyVersion": { "type": "integer", "format": "int32" } }, "additionalProperties": true } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Not Found", "content": { "*/*": { "schema": { "type": "string" } } } }, "405": { "description": "Method Not Allowed", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "Not Acceptable", "content": { "*/*": { "schema": { "type": "string" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Signing is enabled but the signing operation failed.", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 } } ], "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" } } } }, "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" } } } }, "400": { "description": "Invalid request, malformed or unparseable PDF, password-protected PDF, PDF without pages, or sign=true on a PDF request.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The named policy does not exist.", "content": { "*/*": { "schema": { "type": "string" } } } }, "405": { "description": "Method Not Allowed", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "Not Acceptable", "content": { "*/*": { "schema": { "type": "string" } } } }, "413": { "description": "Request body, resolved configuration (1 MiB), or encrypted async record exceeds its size limit.", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "429": { "description": "Account async queue capacity reached.", "content": { "text/plain": { "schema": { "type": "string" } } }, "headers": { "Retry-After": { "schema": { "example": 5, "type": "integer", "format": "int32" } } } }, "500": { "description": "Signing is enabled but the signing operation failed.", "content": { "*/*": { "schema": { "type": "string" } } } }, "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" } } } } } } }, "/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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": "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": "Array of legal holds, most recently set first. With all_users, each hold also has an owner field.", "content": { "application/json": { "schema": { "oneOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/LegalHoldResponse" } }, { "type": "array", "items": { "$ref": "#/components/schemas/OwnedLegalHoldResponse" } } ] } } } }, "400": { "description": "Both owner and all_users were given.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "array", "items": { "$ref": "#/components/schemas/LegalHoldResponse" } } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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 user. 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" } } } }, "400": { "description": "Required fields are missing or the scope type is invalid.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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/LegalHoldResponse" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "A hold with this reference exists, or an evidence/hold operation is active or requires recovery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LegalHoldResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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": { "200": { "description": "The hold was released." }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "No hold with the given reference exists for this user." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "An evidence or hold operation is active or requires recovery." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "No hold with the given reference exists for this user.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LegalHoldResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "older_than_days is missing or negative.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The caller is not an administrator, or ledger deletion is disabled for this deployment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "Not Found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "An evidence or hold operation is active or requires recovery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "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" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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 email 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": "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": { "oneOf": [ { "$ref": "#/components/schemas/GetLedgerResponse" }, { "type": "object", "properties": { "chains": { "type": "array", "items": { "$ref": "#/components/schemas/OwnedLedgerEntryView" } }, "total": { "type": "integer", "format": "int32" } } } ] } } } }, "400": { "description": "all_users was combined with owner or q.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": { "200": { "description": "The document's ledger chain was deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The caller is not an administrator, or ledger deletion is disabled for this deployment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "No ledger chain for that document exists for this user.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "An evidence or hold operation is active or requires recovery.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "423": { "description": "The chain is protected by an active legal hold. Release the hold before deleting.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "get": { "tags": [ "Redaction Ledger" ], "summary": "Get a document's ledger chain.", "description": "Returns the full ordered chain of ledger entries for a document, along with whether the hash chain currently verifies. 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" } } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LedgerChainResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "", "content": { "*/*": { "schema": { "type": "string" } } } }, "405": { "description": "Method Not Allowed", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "Not Acceptable", "content": { "*/*": { "schema": { "type": "string" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Not Found", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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). 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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "", "content": { "*/*": { "schema": { "type": "string" } } } }, "405": { "description": "Method Not Allowed", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "Not Acceptable", "content": { "*/*": { "schema": { "type": "string" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/lists": { "get": { "tags": [ "Custom Lists" ], "summary": "Get the names of existing lists.", "description": "Get the names of the caller's custom lists, all of them. Admins may list another user's lists with owner, or every user's with all_users=true, which is paged with offset and limit, returns each list's name and 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": "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": "List names; with all_users, each list's name and owner.", "content": { "application/json": { "schema": { "oneOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "array", "items": { "$ref": "#/components/schemas/OwnedNameResponse" } } ] } } } }, "400": { "description": "Both owner and all_users were given.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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.", "content": { "application/json": { "schema": { "type": "string" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The given list does not exist.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "get": { "tags": [ "Custom Lists" ], "summary": "Get the contents of a list.", "description": "Get the contents of a list with the provided name. 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/GetListsResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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/GetListsResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "post": { "tags": [ "Custom Lists" ], "summary": "Create or update a list.", "description": "Create a list whose contents are the request body, or overwrite it if a list with the same name already exists. 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": "description", "in": "query", "required": false, "schema": { "type": "string", "default": "" } }, { "name": "owner", "in": "query", "required": false, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } }, "responses": { "200": { "description": "An existing list with the same name was updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "201": { "description": "The list was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "The list name is empty, the list contains too many items (maximum 100), or an item is too long (maximum 50 characters).", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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" } } } }, "406": { "description": "No representation matches Accept." }, "412": { "description": "The maximum number of lists already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/policies": { "get": { "tags": [ "Policies" ], "summary": "Get the names of existing policies.", "description": "Returns the names of the caller's policies, paged. Admins may list another user's policies by passing that user's email as owner, or every user's with all_users=true, which returns each policy's name and owner and requires ADMIN_CROSS_USER_ACCESS_ENABLED. With managed=true, returns the names of the built-in managed policies instead. 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": "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": "The names of the policies; with all_users, objects naming each policy and its owner.", "content": { "application/json": { "schema": { "oneOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "array", "items": { "$ref": "#/components/schemas/OwnedNameResponse" } } ] } } } }, "400": { "description": "Both owner and all_users were given, or managed was combined with either.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "post": { "tags": [ "Policies" ], "summary": "Create or update a policy.", "description": "Saves the policy supplied in the request body under the given name, overwriting any existing policy of the same name. The policy is validated before it is stored. description (up to 200 characters) and notes (up to 1000) are optional; when updating, leaving either out keeps its current value. Admins may save into another user's account by passing that user's email as owner. Requires the `policies:write` scope.", "operationId": "save", "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": "description", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "notes", "in": "query", "required": false, "schema": { "type": "string" } } ], "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 saved and is now active. A policy_activated audit event is recorded." }, "400": { "description": "The policy name is missing or invalid, the policy is invalid, or the description or notes are too long.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 } } } } } }, "400": { "description": "The PhiSQL failed to parse/compile, or the compiled policy failed validation.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 email 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": { "200": { "description": "The policy was deleted." }, "400": { "description": "The policy name is missing.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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 email as owner. The policy's description and notes are at /api/policies/{policyName}/details. 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 } } } }, "400": { "description": "The policy name is missing.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "A policy with the given name does not exist.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 email 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.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyDetailsResponse" } } } }, "400": { "description": "The new name is missing or invalid.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "A policy with the new name already exists.", "content": { "application/json": { "schema": { "type": "object" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/api/policies/{policyName}/details": { "get": { "tags": [ "Policies" ], "summary": "Get a policy's details.", "description": "Returns a policy's name, description, notes, current revision, whether it is a built-in managed policy, and when it was created and last updated: everything except the policy itself. A name starting with managed_ returns that managed policy's details. Admins may read another user's by passing that user's email 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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "There is no such policy, or the owner does not exist or the caller may not reach it.", "content": { "application/json": { "schema": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. Managed policies cannot be changed. Admins may change another user's by passing that user's email as owner. Requires the `policies:write` scope.", "operationId": "setDetails", "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" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyDetailsRequest" } } } }, "responses": { "200": { "description": "The policy's details after the change.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyDetailsResponse" } } } }, "400": { "description": "The description or notes are too long.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "There is no such policy, or the owner does not exist or the caller may not reach it.", "content": { "application/json": { "schema": { "type": "object" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The policy is a managed policy.", "content": { "application/json": { "schema": { "type": "object" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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": { "op": { "type": "string", "enum": [ "add", "remove", "replace" ] }, "path": { "type": "string" }, "value": {} } } }, "from": { "type": "integer", "format": "int32" }, "to": { "type": "integer", "format": "int32" } } } } } }, "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": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The policy or a requested revision does not exist.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 rollback is audited. 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": "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.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyRollbackResponse" } } } }, "400": { "description": "The policy name or target revision is missing.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The policy or the target revision does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyRollbackResponse" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "Managed policies cannot be rolled back.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PolicyRollbackResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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, and its content hash. 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": "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": "Array of version summaries, most recent first.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PolicyVersionSummary" } } } } }, "400": { "description": "The policy name is missing.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The named policy does not exist, or a non-admin caller named another user as owner.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PolicyVersionSummary" } } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 } } } }, "400": { "description": "The policy name is missing.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "The policy or the requested revision does not exist.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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 email 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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. Admins may replace another user's lists by passing that user's email 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": { "*/*": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "The request body is malformed, a list has too many terms, or a term is too long.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "*/*": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. Admins may append to another user's lists by passing that user's email 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": { "*/*": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "400": { "description": "The request body is malformed, the resulting list has too many terms, or a term is too long.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "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": { "*/*": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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 email 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" } } } }, "400": { "description": "Missing or invalid request fields.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Missing or invalid API key.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "Insufficient scope, disabled API, or access denied." }, "404": { "description": "Named policy not found, or owner not found.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold settings:read, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. 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" } } } }, "400": { "description": "A value is invalid. The body says which, and nothing was changed.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold settings:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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", "additionalProperties": true }, "keyId": { "type": "string" }, "pem": { "type": "string" } } } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold signing:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "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" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "Unauthorized", "content": { "*/*": { "schema": { "type": "string" } } } }, "404": { "description": "No key with that id is retained.", "content": { "application/json": { "schema": { "type": "string" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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": "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:read, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. The user has no password and authenticates with API keys; create one with POST /api/users/{username}/api-keys. 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" } } } }, "400": { "description": "The username is missing or reserved, the role is not user or admin, or a password was sent.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "409": { "description": "A user with that username already exists, active or deactivated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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/UserResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:read, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no user with that username." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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. 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": "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 keys.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GetApiKeysResponse" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "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" } } } }, "404": { "description": "There is no user with that username." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. 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" } } } }, "400": { "description": "No scopes were given, or one of them is not a scope.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold api-keys:write, the caller is not an administrator, or a requested scope is not held by the calling key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no active user with that username." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no user with that username." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The user is already deactivated, is the caller, or is the last active administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no user with that username." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The user is already active.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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" } } } }, "400": { "description": "The role is missing or is not user or admin.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold users:write, or the caller is not an administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "There is no user with that username." }, "406": { "description": "No representation matches Accept." }, "409": { "description": "The user is the last active administrator.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } }, "/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." }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold webhooks:write.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "The owner does not exist, or the caller may not reach it." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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" } } } }, "400": { "description": "Bad Request", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold webhooks:read.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "The owner does not exist, or the caller may not reach it." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } }, "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. 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" } } } }, "400": { "description": "The URL or secret is missing, the URL is invalid or not permitted, or the secret is too short. The message says which.", "content": { "*/*": { "schema": { "type": "string" } } } }, "401": { "description": "The Authorization header is absent or the API key is not recognized.", "content": { "*/*": { "schema": { "type": "string" } } } }, "403": { "description": "The key does not hold webhooks:write.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericResponse" } } } }, "404": { "description": "The owner does not exist, or the caller may not reach it." }, "406": { "description": "No representation matches Accept." }, "413": { "description": "Content Too Large", "content": { "*/*": { "schema": { "type": "string" } } } }, "415": { "description": "Unsupported Media Type", "content": { "*/*": { "schema": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "content": { "*/*": { "schema": { "type": "string" } } } }, "503": { "description": "Service Unavailable", "content": { "*/*": { "schema": { "type": "string" } } } } } } } }, "components": { "schemas": { "ApiKeyResponse": { "type": "object", "properties": { "bootstrap": { "type": "boolean" }, "created": { "type": "string", "format": "date-time" }, "id": { "type": "string" }, "prefix": { "type": "string" }, "scopes": { "type": "array", "items": { "type": "string" } } } }, "AuditEventView": { "properties": { "apiKeyId": { "type": "string" }, "associatedObject": { "type": "string" }, "clientIpAddress": { "type": "string" }, "details": { "type": "string" }, "event": { "type": "string" }, "requestId": { "type": "string" }, "timestamp": { "type": "string", "format": "date-time" } } }, "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" } } }, "CreateApiKeyRequest": { "type": "object", "properties": { "scopes": { "type": "array", "items": { "type": "string" } } } }, "CreateUserRequest": { "type": "object", "properties": { "email": { "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": { "role": { "type": "string" }, "username": { "type": "string" } } }, "GenericResponse": { "type": "object", "properties": { "message": { "type": "string" } } }, "GetAllUsersContextsResponse": { "properties": { "contexts": { "type": "array", "items": { "$ref": "#/components/schemas/OwnedNameResponse" } } } }, "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": "int32" } } }, "GetContextResponse": { "properties": { "filterTypes": { "type": "object", "additionalProperties": { "type": "integer", "format": "int64" } }, "size": { "type": "integer", "format": "int64" }, "untyped": { "type": "integer", "format": "int64" } } }, "GetContextsResponse": { "properties": { "contexts": { "type": "array", "items": { "type": "string" } } } }, "GetDocumentsResponse": { "properties": { "pendingRedactedDocuments": { "type": "array", "items": { "$ref": "#/components/schemas/PendingRedactedDocuments" } } } }, "GetLedgerResponse": { "properties": { "chains": { "type": "array", "items": { "$ref": "#/components/schemas/LedgerEntryView" } }, "total": { "type": "integer", "format": "int32" } } }, "GetListsResponse": { "type": "object", "properties": { "lists": { "type": "array", "items": { "type": "string" } } } }, "GetRedactionStatusResponse": { "properties": { "documentId": { "type": "string" }, "effectiveConfigurationHash": { "type": "string" }, "error": { "type": "string" }, "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": { "type": "array", "items": { "$ref": "#/components/schemas/LedgerEntryView" } }, "hashChainValid": { "type": "boolean" }, "signaturesValid": { "type": "boolean" }, "signedEntries": { "type": "integer", "format": "int32" }, "unsignedEntries": { "type": "integer", "format": "int32" }, "valid": { "type": "boolean" } } }, "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" }, "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" } } } }, "LegalHoldRequest": { "type": "object", "properties": { "reason": { "type": "string" }, "reference": { "type": "string" }, "scopeType": { "type": "string" }, "scopeValue": { "type": "string" } } }, "LegalHoldResponse": { "type": "object", "properties": { "reason": { "type": "string" }, "reference": { "type": "string" }, "scopeType": { "type": "string" }, "scopeValue": { "type": "string" }, "setAt": { "type": "string", "format": "date-time" } } }, "OwnedLedgerEntryView": { "properties": { "type": { "type": "string" }, "documentHash": { "type": "string" }, "documentId": { "type": "string" }, "effectiveHash": { "type": "string" }, "filename": { "type": "string" }, "hash": { "type": "string" }, "owner": { "type": "string" }, "policyContentHash": { "type": "string" }, "policyName": { "type": "string" }, "policyVersion": { "type": "integer", "format": "int32" }, "previousHash": { "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": { "type": "string" }, "reason": { "type": "string" }, "reference": { "type": "string" }, "scopeType": { "type": "string" }, "scopeValue": { "type": "string" }, "setAt": { "type": "string", "format": "date-time" } } }, "OwnedNameResponse": { "properties": { "name": { "type": "string" }, "owner": { "type": "string" } } }, "PendingRedactedDocuments": { "properties": { "documentId": { "type": "string" }, "fileName": { "type": "string" }, "status": { "type": "string" }, "timestamp": { "type": "string", "format": "date-time" } } }, "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" }, "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" } } }, "PolicyVersionSummary": { "type": "object", "properties": { "capturedTimestamp": { "type": "string", "format": "date-time" }, "contentHash": { "type": "string" }, "revision": { "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" } }, "neverRedact": { "type": "array", "items": { "type": "string" } } } }, "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" } } }, "SetApiKeyScopesRequest": { "type": "object", "properties": { "scopes": { "type": "array", "items": { "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": { "diffuseCountsEnabled": { "type": "boolean" }, "phieldApiKeySet": { "type": "boolean" }, "phieldEnabled": { "type": "boolean" }, "phieldOrganization": { "type": "string" }, "phieldSourceId": { "type": "string" }, "phieldUrl": { "type": "string" }, "signingEnabled": { "type": "boolean" }, "warnings": { "type": "array", "items": { "type": "string" } }, "webhookAllowlist": { "type": "string" } } }, "StatusResponse": { "type": "object", "properties": { "applicationVersion": { "type": "string" }, "gitCommit": { "type": "string" }, "redactionPolicySchemaVersion": { "type": "string" }, "status": { "type": "string" } } }, "UpdateSettingsRequest": { "type": "object", "properties": { "diffuseCountsEnabled": { "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" }, "role": { "type": "string" }, "username": { "type": "string" } } }, "WebhookResponse": { "type": "object", "properties": { "url": { "type": "string" }, "secretSet": { "type": "boolean" } } } }, "securitySchemes": { "apiKey": { "description": "A Philter API key, sent as `Authorization: Bearer `. Create one on the API Keys page of the dashboard.", "type": "http", "scheme": "bearer" } } }, "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": "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": "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": "Operations for creating and managing contexts.", "name": "Contexts" }, { "description": "Create and manage users. Requires an administrator, except for reading the calling key's own user.", "name": "Users" }, { "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" } ] }