# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema openapi: 3.1.0 info: title: agent-call SaaS Recording Handoff API version: 1.0.0 description: >- Internal storage handshake. Business results still return through RabbitMQ. servers: - url: https://saas.internal security: - bearerAuth: [] paths: /internal/v1/outbound/recording-uploads: post: operationId: createRecordingUpload parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/RequestId' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/UploadRequest'} responses: '201': description: Upload session created or existing session returned headers: {Cache-Control: {schema: {const: no-store}}} content: application/json: schema: {$ref: '#/components/schemas/UploadSession'} '200': description: Existing upload session content: application/json: schema: {$ref: '#/components/schemas/UploadSession'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '409': {$ref: '#/components/responses/Conflict'} /internal/v1/outbound/recording-uploads/{upload_id}/complete: post: operationId: completeRecordingUpload parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/RequestId' - $ref: '#/components/parameters/IdempotencyKey' - name: upload_id in: path required: true schema: {$ref: '#/components/schemas/Id'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CompleteRequest'} responses: '200': description: Object independently verified content: application/json: schema: {$ref: '#/components/schemas/VerifiedUpload'} '409': {$ref: '#/components/responses/Conflict'} '410': {$ref: '#/components/responses/Expired'} '422': {$ref: '#/components/responses/Unprocessable'} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} components: securitySchemes: bearerAuth: {type: http, scheme: bearer} parameters: TenantId: name: X-Tenant-ID in: header required: true schema: {type: string} RequestId: name: X-Request-ID in: header required: true schema: {type: string} IdempotencyKey: name: Idempotency-Key in: header required: true schema: {type: string} schemas: Id: type: string minLength: 1 maxLength: 128 pattern: '^[^\s/\\]+$' UploadRequest: type: object additionalProperties: false required: - recording_id - call_id - content_type - size_bytes - checksum_algorithm - checksum - channels - sample_rate_hz - duration_ms properties: recording_id: {$ref: '#/components/schemas/Id'} call_id: {$ref: '#/components/schemas/Id'} content_type: {type: string, const: audio/wav} size_bytes: {type: integer, minimum: 1} checksum_algorithm: {type: string, const: SHA-256} checksum: {type: string, pattern: '^[0-9a-f]{64}$'} channels: {type: integer, const: 1} sample_rate_hz: {type: integer, minimum: 8000} duration_ms: {type: integer, minimum: 1} UploadSession: type: object required: - upload_id - recording_id - expires_at - upload_method - upload_url - required_headers - constraints properties: upload_id: {$ref: '#/components/schemas/Id'} recording_id: {$ref: '#/components/schemas/Id'} expires_at: {type: string, format: date-time} upload_method: {const: PUT} upload_url: {type: string, format: uri} required_headers: {type: object} constraints: {type: object} oss_id: {type: [string, 'null']} CompleteRequest: type: object additionalProperties: false required: [recording_id, size_bytes, checksum_algorithm, checksum] properties: recording_id: {$ref: '#/components/schemas/Id'} size_bytes: {type: integer, minimum: 1} checksum_algorithm: {const: SHA-256} checksum: {type: string, pattern: '^[0-9a-f]{64}$'} etag: {type: [string, 'null']} VerifiedUpload: type: object required: [upload_id, recording_id, status, oss_id, verified_at] properties: upload_id: {$ref: '#/components/schemas/Id'} recording_id: {$ref: '#/components/schemas/Id'} status: {const: verified} oss_id: {type: string} verified_at: {type: string, format: date-time} Problem: type: object required: [type, title, status, code, detail, request_id, retryable] properties: type: {type: string} title: {type: string} status: {type: integer} code: {type: string} detail: {type: string} request_id: {type: string} retryable: {type: boolean} responses: Unauthorized: {description: Unauthorized} Forbidden: {description: Forbidden} Conflict: {description: Idempotency conflict} Expired: {description: Upload expired} Unprocessable: {description: Object failed independent verification}