openapi: 3.1.0 info: {title: HAN Message Safety Internal API, version: 2.0.0} servers: [{url: https://processing.internal:8443}] security: [{ServiceToken: []}] paths: /internal/safety/v2/messages/check: post: operationId: checkMessage parameters: [{$ref: '#/components/parameters/RequestId'}, {$ref: '#/components/parameters/Traceparent'}] requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CheckRequest'} responses: '200': {$ref: '#/components/responses/Allow'} '202': {$ref: '#/components/responses/Pending'} '400': {$ref: '#/components/responses/Error'} '401': {$ref: '#/components/responses/Error'} '403': {$ref: '#/components/responses/Deny'} '409': {$ref: '#/components/responses/Error'} '429': {$ref: '#/components/responses/Error'} '500': {$ref: '#/components/responses/Error'} '503': {$ref: '#/components/responses/Error'} /internal/safety/v2/messages/tasks/{task_id}: get: operationId: getMessageSafetyTask parameters: - {name: task_id, in: path, required: true, schema: {type: string, format: uuid}} - {$ref: '#/components/parameters/RequestId'} - {$ref: '#/components/parameters/Traceparent'} responses: '200': {$ref: '#/components/responses/Allow'} '202': {$ref: '#/components/responses/Pending'} '400': {$ref: '#/components/responses/Error'} '401': {$ref: '#/components/responses/Error'} '403': {$ref: '#/components/responses/Deny'} '404': {$ref: '#/components/responses/Error'} '429': {$ref: '#/components/responses/Error'} '500': {$ref: '#/components/responses/Error'} '503': {$ref: '#/components/responses/Error'} /health/live: get: security: [] responses: '200': description: Process is alive content: {application/json: {schema: {type: object, additionalProperties: false, required: [status], properties: {status: {const: ok}}}}} /health/ready: get: security: [] responses: '200': {$ref: '#/components/responses/Health'} '503': {$ref: '#/components/responses/Health'} components: securitySchemes: ServiceToken: {type: apiKey, in: header, name: X-Service-Token} parameters: RequestId: {name: X-Request-ID, in: header, required: false, schema: {type: string, format: uuid}} Traceparent: {name: traceparent, in: header, required: false, schema: {type: string, maxLength: 256}} schemas: Attachment: type: object additionalProperties: false required: [attachment_id, quarantine_object_key, quarantine_version_id, quarantine_etag, mime_type, size_bytes, checksum] properties: attachment_id: {type: string, format: uuid} quarantine_object_key: {type: string, minLength: 1, maxLength: 1024} quarantine_version_id: {type: string, minLength: 1, maxLength: 512} quarantine_etag: {type: string, minLength: 1, maxLength: 512} mime_type: {enum: [image/jpeg, image/png, image/webp, image/heic, image/heif, application/pdf]} size_bytes: {type: integer, minimum: 1, maximum: 5242880} checksum: {type: string, pattern: '^sha256:[0-9a-f]{64}$'} TextCheck: type: object additionalProperties: false required: [message_id, content_kind, text, attachment] properties: message_id: {type: string, format: uuid} content_kind: {const: text} text: {type: string, minLength: 1, maxLength: 10000} attachment: {type: 'null'} FileCheck: type: object additionalProperties: false required: [message_id, content_kind, text, attachment] properties: message_id: {type: string, format: uuid} content_kind: {const: file} text: {const: ''} attachment: {$ref: '#/components/schemas/Attachment'} CheckRequest: oneOf: [{$ref: '#/components/schemas/TextCheck'}, {$ref: '#/components/schemas/FileCheck'}] discriminator: {propertyName: content_kind, mapping: {text: '#/components/schemas/TextCheck', file: '#/components/schemas/FileCheck'}} Verdict: type: object additionalProperties: false required: [verdict, processing_mode, config_version, rule_id, rules_version] properties: verdict: {enum: [allow, deny]} processing_mode: {enum: [standard, mock]} config_version: {type: integer, minimum: 1} rule_id: {type: string} reason_code: {enum: [message_blocked]} rules_version: {type: string} Pending: type: object additionalProperties: false required: [verdict, processing_mode, config_version, task_id, poll_after_ms, expires_at, rules_version] properties: verdict: {const: pending} processing_mode: {const: standard} config_version: {type: integer} task_id: {type: string, format: uuid} poll_after_ms: {type: integer, minimum: 1} expires_at: {type: string, format: date-time} rules_version: {type: string} Error: type: object additionalProperties: false required: [error] properties: error: type: object additionalProperties: false required: [code, message, request_id, details] properties: code: {enum: [validation_error, service_unauthorized, task_not_found, safety_request_conflict, rate_limit_exceeded, dependency_unavailable, task_failed, internal_error]} message: {type: string} request_id: {type: string} details: {type: object} Health: type: object required: [status, processing_mode, config_version, components, capabilities] properties: status: {enum: [ok, degraded, not_ready]} processing_mode: {enum: [standard, mock]} config_version: {type: integer} components: {type: object, additionalProperties: {enum: [ok, degraded, down, bypassed]}} capabilities: {type: object, additionalProperties: {enum: [ready, unavailable, bypassed]}} mock_policy: {type: object, additionalProperties: {enum: [allow, deny]}} responses: Allow: description: Sticky allow verdict content: {application/json: {schema: {$ref: '#/components/schemas/Verdict'}}} Deny: description: Sticky domain deny content: {application/json: {schema: {$ref: '#/components/schemas/Verdict'}}} Pending: description: Asynchronous file check headers: Location: {required: true, schema: {type: string}} Retry-After: {required: true, schema: {type: integer}} Cache-Control: {required: true, schema: {const: no-store}} content: {application/json: {schema: {$ref: '#/components/schemas/Pending'}}} Error: description: Error envelope content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}} Health: description: Capability-aware readiness content: {application/json: {schema: {$ref: '#/components/schemas/Health'}}}