{ "openapi": "3.1.0", "info": { "title": "YoVision Sense Audit Relay", "version": "1.0.0", "description": "Internal, signed and idempotent delivery of redacted Sense device audit facts to Bell." }, "paths": { "/internal/v1/audit-events:batch": { "post": { "operationId": "receiveSenseAuditBatch", "description": "Accepts 1-100 events in a body no larger than 1048576 bytes. The request deadline is 10 seconds. HMAC clock skew is at most 300 seconds and nonce receipts live for 600 seconds.", "parameters": [ {"name": "X-YoVision-Key-Id", "in": "header", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}}, {"name": "X-YoVision-Timestamp", "in": "header", "required": true, "description": "Unix seconds", "schema": {"type": "string", "pattern": "^[0-9]{10,}$"}}, {"name": "X-YoVision-Nonce", "in": "header", "required": true, "description": "16-48 random bytes encoded as unpadded base64url", "schema": {"type": "string", "minLength": 22, "maxLength": 64, "pattern": "^[A-Za-z0-9_-]+$"}}, {"name": "X-YoVision-Signature", "in": "header", "required": true, "description": "Unpadded base64url HMAC-SHA256 over POST, path, timestamp, nonce and lowercase SHA-256 body digest joined by newlines", "schema": {"type": "string", "minLength": 43, "maxLength": 43}} ], "requestBody": { "required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BatchRequest"}}} }, "responses": { "200": {"description": "Stored, duplicate or permanently rejected per item. Same key ID, nonce and request digest returns the original response.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BatchResponse"}}}}, "400": {"description": "Malformed batch envelope"}, "401": {"description": "Invalid key, timestamp, nonce or signature"}, "409": {"description": "Same key ID and nonce used with a different request digest; error is replay_conflict"}, "413": {"description": "Body exceeds 1048576 bytes"}, "503": {"description": "Bell cannot atomically persist the batch and receipt"} } } } }, "components": { "schemas": { "BatchRequest": { "type": "object", "additionalProperties": false, "required": ["events"], "properties": {"events": {"type": "array", "minItems": 1, "maxItems": 100, "items": {"$ref": "#/components/schemas/Envelope"}}} }, "Envelope": { "type": "object", "additionalProperties": false, "required": ["schema_version", "event"], "properties": { "schema_version": {"type": "integer", "enum": [1, 2]}, "event": {"$ref": "#/components/schemas/AuditEvent"} } }, "AuditEvent": { "type": "object", "additionalProperties": false, "required": ["event_id", "event_type", "tenant_id", "site_id", "device_id", "actor", "reason", "trace_id", "aggregate_generation", "projection_versions", "data", "occurred_at"], "properties": { "event_id": {"type": "string", "pattern": "^audit_[0-9a-f]{32}$"}, "event_type": {"type": "string", "enum": ["device.created", "device.desired_state.accepted", "device.configuration.accepted"]}, "tenant_id": {"$ref": "#/components/schemas/LogicalId"}, "site_id": {"$ref": "#/components/schemas/LogicalId"}, "device_id": {"$ref": "#/components/schemas/LogicalId"}, "actor": {"type": "object", "additionalProperties": false, "required": ["type", "id"], "properties": {"type": {"type": "string", "enum": ["user", "service", "system"]}, "id": {"type": "string", "minLength": 1, "maxLength": 200}}}, "reason": {"type": ["string", "null"], "maxLength": 500}, "trace_id": {"type": ["string", "null"], "maxLength": 128}, "aggregate_generation": {"type": "integer", "minimum": 1}, "projection_versions": {"type": "object", "additionalProperties": false, "required": ["quota_source_version", "area_policy_source_version"], "properties": {"quota_source_version": {"type": ["integer", "null"], "minimum": 1}, "area_policy_source_version": {"type": ["integer", "null"], "minimum": 1}}}, "data": {"type": "object"}, "occurred_at": {"type": "string", "format": "date-time"} } }, "LogicalId": {"type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"}, "BatchResponse": { "type": "object", "additionalProperties": false, "required": ["results"], "properties": {"results": {"type": "array", "minItems": 1, "maxItems": 100, "items": {"$ref": "#/components/schemas/Result"}}} }, "Result": { "type": "object", "additionalProperties": false, "required": ["event_id", "status"], "properties": { "event_id": {"type": "string"}, "status": {"type": "string", "enum": ["accepted", "duplicate", "rejected"]}, "error_code": {"type": "string", "pattern": "^[a-z][a-z0-9_]{0,63}$"} } } } }, "x-yovision-signature": { "canonical": "METHOD\\nPATH\\nTIMESTAMP\\nNONCE\\nLOWERCASE_SHA256_BODY", "algorithm": "HMAC-SHA256", "encoding": "base64url-no-padding", "clock_skew_seconds": 300, "receipt_ttl_seconds": 600 }, "x-yovision-delivery": { "lease_seconds": 30, "initial_retry_seconds": 1, "maximum_retry_seconds": 300, "successful_statuses": ["accepted", "duplicate"], "permanent_status": "rejected" } }