@giveitsmaller/contracts 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/asyncapi/events.yaml +455 -41
  2. package/availability/availability.json +586 -91
  3. package/dist/asyncapi/OperationType.d.ts +5 -1
  4. package/dist/asyncapi/OperationType.js +4 -0
  5. package/dist/openapi/models/AudioWatermarkDecodeRequest.d.ts +1 -1
  6. package/dist/openapi/models/AudioWatermarkDecodeRequest.js +1 -1
  7. package/dist/openapi/models/AudioWatermarkDecodeResponse.d.ts +1 -1
  8. package/dist/openapi/models/AudioWatermarkDecodeResponse.js +1 -1
  9. package/dist/openapi/models/AuthErrorResponse.d.ts +20 -1
  10. package/dist/openapi/models/AuthErrorResponse.js +1 -1
  11. package/dist/openapi/models/AuthErrorType.d.ts +1 -1
  12. package/dist/openapi/models/AuthErrorType.js +1 -1
  13. package/dist/openapi/models/AvailabilityValue.d.ts +1 -1
  14. package/dist/openapi/models/AvailabilityValue.js +1 -1
  15. package/dist/openapi/models/BalanceExhaustedResponse.d.ts +20 -1
  16. package/dist/openapi/models/BalanceExhaustedResponse.js +1 -1
  17. package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.d.ts +1 -1
  18. package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.js +1 -1
  19. package/dist/openapi/models/CallbackEventType.d.ts +1 -1
  20. package/dist/openapi/models/CallbackEventType.js +1 -1
  21. package/dist/openapi/models/ConnectionSource.d.ts +1 -1
  22. package/dist/openapi/models/ConnectionSource.js +1 -1
  23. package/dist/openapi/models/ContactRequest.d.ts +1 -1
  24. package/dist/openapi/models/ContactRequest.js +1 -1
  25. package/dist/openapi/models/ContactSubject.d.ts +1 -1
  26. package/dist/openapi/models/ContactSubject.js +1 -1
  27. package/dist/openapi/models/ContactValidationErrorResponse.d.ts +1 -1
  28. package/dist/openapi/models/ContactValidationErrorResponse.js +1 -1
  29. package/dist/openapi/models/CreateExternalImport403Response.d.ts +1 -1
  30. package/dist/openapi/models/CreateExternalImport403Response.js +1 -1
  31. package/dist/openapi/models/CreateExternalImport422Response.d.ts +23 -0
  32. package/dist/openapi/models/CreateExternalImport422Response.js +51 -0
  33. package/dist/openapi/models/CreateWorkflow422Response.d.ts +4 -2
  34. package/dist/openapi/models/CreateWorkflow422Response.js +15 -1
  35. package/dist/openapi/models/CreditTransaction.d.ts +1 -1
  36. package/dist/openapi/models/CreditTransaction.js +1 -1
  37. package/dist/openapi/models/CreditTransactionSourceBucket.d.ts +1 -1
  38. package/dist/openapi/models/CreditTransactionSourceBucket.js +1 -1
  39. package/dist/openapi/models/CreditsBalanceResponse.d.ts +1 -1
  40. package/dist/openapi/models/CreditsBalanceResponse.js +1 -1
  41. package/dist/openapi/models/CreditsBalanceSuccessEnvelope.d.ts +1 -1
  42. package/dist/openapi/models/CreditsBalanceSuccessEnvelope.js +1 -1
  43. package/dist/openapi/models/CreditsUsageResponse.d.ts +1 -1
  44. package/dist/openapi/models/CreditsUsageResponse.js +1 -1
  45. package/dist/openapi/models/CreditsUsageSuccessEnvelope.d.ts +1 -1
  46. package/dist/openapi/models/CreditsUsageSuccessEnvelope.js +1 -1
  47. package/dist/openapi/models/Delivery.d.ts +1 -1
  48. package/dist/openapi/models/Delivery.js +1 -1
  49. package/dist/openapi/models/DeliveryOutputRef.d.ts +1 -1
  50. package/dist/openapi/models/DeliveryOutputRef.js +1 -1
  51. package/dist/openapi/models/DeliveryPlan.d.ts +1 -1
  52. package/dist/openapi/models/DeliveryPlan.js +1 -1
  53. package/dist/openapi/models/DeliveryPlanOutput.d.ts +1 -1
  54. package/dist/openapi/models/DeliveryPlanOutput.js +1 -1
  55. package/dist/openapi/models/DeliveryPlanReason.d.ts +1 -1
  56. package/dist/openapi/models/DeliveryPlanReason.js +1 -1
  57. package/dist/openapi/models/DeliverySelection.d.ts +1 -1
  58. package/dist/openapi/models/DeliverySelection.js +1 -1
  59. package/dist/openapi/models/ErrorEnvelope.d.ts +20 -1
  60. package/dist/openapi/models/ErrorEnvelope.js +1 -1
  61. package/dist/openapi/models/EstimateQuality.d.ts +1 -1
  62. package/dist/openapi/models/EstimateQuality.js +1 -1
  63. package/dist/openapi/models/EstimateRange.d.ts +1 -1
  64. package/dist/openapi/models/EstimateRange.js +1 -1
  65. package/dist/openapi/models/ExternalDestination.d.ts +1 -1
  66. package/dist/openapi/models/ExternalDestination.js +1 -1
  67. package/dist/openapi/models/ExternalImportCreatedResponse.d.ts +1 -1
  68. package/dist/openapi/models/ExternalImportCreatedResponse.js +1 -1
  69. package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.d.ts +1 -1
  70. package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.js +1 -1
  71. package/dist/openapi/models/ExternalImportRequest.d.ts +1 -1
  72. package/dist/openapi/models/ExternalImportRequest.js +1 -1
  73. package/dist/openapi/models/ExternalImportToken.d.ts +1 -1
  74. package/dist/openapi/models/ExternalImportToken.js +1 -1
  75. package/dist/openapi/models/ExternalSource.d.ts +1 -1
  76. package/dist/openapi/models/ExternalSource.js +1 -1
  77. package/dist/openapi/models/FeatureNotAvailableResponse.d.ts +26 -3
  78. package/dist/openapi/models/FeatureNotAvailableResponse.js +1 -1
  79. package/dist/openapi/models/FeatureTierRestrictedResponse.d.ts +20 -1
  80. package/dist/openapi/models/FeatureTierRestrictedResponse.js +1 -1
  81. package/dist/openapi/models/FeatureViolation.d.ts +1 -1
  82. package/dist/openapi/models/FeatureViolation.js +1 -1
  83. package/dist/openapi/models/JobDefinition.d.ts +17 -6
  84. package/dist/openapi/models/JobDefinition.js +1 -1
  85. package/dist/openapi/models/JobDownload.d.ts +1 -1
  86. package/dist/openapi/models/JobDownload.js +1 -1
  87. package/dist/openapi/models/JobInputV2.d.ts +16 -1
  88. package/dist/openapi/models/JobInputV2.js +1 -1
  89. package/dist/openapi/models/JobOutputSource.d.ts +1 -1
  90. package/dist/openapi/models/JobOutputSource.js +1 -1
  91. package/dist/openapi/models/JobResponse.d.ts +1 -1
  92. package/dist/openapi/models/JobResponse.js +1 -1
  93. package/dist/openapi/models/JobStatus.d.ts +1 -1
  94. package/dist/openapi/models/JobStatus.js +1 -1
  95. package/dist/openapi/models/JobType.d.ts +1 -1
  96. package/dist/openapi/models/JobType.js +1 -1
  97. package/dist/openapi/models/LivenessResponse.d.ts +1 -1
  98. package/dist/openapi/models/LivenessResponse.js +1 -1
  99. package/dist/openapi/models/LoginUser200Response.d.ts +1 -1
  100. package/dist/openapi/models/LoginUser200Response.js +1 -1
  101. package/dist/openapi/models/LoginUser200ResponseData.d.ts +1 -1
  102. package/dist/openapi/models/LoginUser200ResponseData.js +1 -1
  103. package/dist/openapi/models/LoginUser200ResponseDataUser.d.ts +1 -1
  104. package/dist/openapi/models/LoginUser200ResponseDataUser.js +1 -1
  105. package/dist/openapi/models/LoginUserRequest.d.ts +1 -1
  106. package/dist/openapi/models/LoginUserRequest.js +1 -1
  107. package/dist/openapi/models/LogoutUser200Response.d.ts +1 -1
  108. package/dist/openapi/models/LogoutUser200Response.js +1 -1
  109. package/dist/openapi/models/MetadataResponse.d.ts +1 -1
  110. package/dist/openapi/models/MetadataResponse.js +1 -1
  111. package/dist/openapi/models/MetadataResponseDimensions.d.ts +1 -1
  112. package/dist/openapi/models/MetadataResponseDimensions.js +1 -1
  113. package/dist/openapi/models/MetadataResponseExif.d.ts +1 -1
  114. package/dist/openapi/models/MetadataResponseExif.js +1 -1
  115. package/dist/openapi/models/MetadataResponseExifGps.d.ts +1 -1
  116. package/dist/openapi/models/MetadataResponseExifGps.js +1 -1
  117. package/dist/openapi/models/MetadataSuccessEnvelope.d.ts +1 -1
  118. package/dist/openapi/models/MetadataSuccessEnvelope.js +1 -1
  119. package/dist/openapi/models/MimeGroupSchema.d.ts +1 -1
  120. package/dist/openapi/models/MimeGroupSchema.js +1 -1
  121. package/dist/openapi/models/MultipartCompleteRequest.d.ts +1 -1
  122. package/dist/openapi/models/MultipartCompleteRequest.js +1 -1
  123. package/dist/openapi/models/MultipartCompleteRequestPartsInner.d.ts +1 -1
  124. package/dist/openapi/models/MultipartCompleteRequestPartsInner.js +1 -1
  125. package/dist/openapi/models/MultipartCompleteResponse.d.ts +1 -1
  126. package/dist/openapi/models/MultipartCompleteResponse.js +1 -1
  127. package/dist/openapi/models/MultipartCompleteSuccessEnvelope.d.ts +1 -1
  128. package/dist/openapi/models/MultipartCompleteSuccessEnvelope.js +1 -1
  129. package/dist/openapi/models/MultipartInitiateRequestMetadataHint.d.ts +1 -1
  130. package/dist/openapi/models/MultipartInitiateRequestMetadataHint.js +1 -1
  131. package/dist/openapi/models/MultipartInitiateResponse.d.ts +20 -4
  132. package/dist/openapi/models/MultipartInitiateResponse.js +1 -1
  133. package/dist/openapi/models/MultipartInitiateSuccessEnvelope.d.ts +1 -1
  134. package/dist/openapi/models/MultipartInitiateSuccessEnvelope.js +1 -1
  135. package/dist/openapi/models/MultipartKeepaliveResponse.d.ts +43 -0
  136. package/dist/openapi/models/MultipartKeepaliveResponse.js +47 -0
  137. package/dist/openapi/models/MultipartKeepaliveSuccessEnvelope.d.ts +46 -0
  138. package/dist/openapi/models/MultipartKeepaliveSuccessEnvelope.js +54 -0
  139. package/dist/openapi/models/MultipartPartListing.d.ts +59 -0
  140. package/dist/openapi/models/MultipartPartListing.js +55 -0
  141. package/dist/openapi/models/MultipartPresignRequest.d.ts +41 -0
  142. package/dist/openapi/models/MultipartPresignRequest.js +43 -0
  143. package/dist/openapi/models/MultipartPresignResponse.d.ts +46 -0
  144. package/dist/openapi/models/MultipartPresignResponse.js +48 -0
  145. package/dist/openapi/models/MultipartPresignSuccessEnvelope.d.ts +46 -0
  146. package/dist/openapi/models/MultipartPresignSuccessEnvelope.js +54 -0
  147. package/dist/openapi/models/MultipartStatusResponse.d.ts +103 -0
  148. package/dist/openapi/models/MultipartStatusResponse.js +76 -0
  149. package/dist/openapi/models/MultipartStatusSuccessEnvelope.d.ts +46 -0
  150. package/dist/openapi/models/MultipartStatusSuccessEnvelope.js +54 -0
  151. package/dist/openapi/models/OperationDefinition.d.ts +1 -1
  152. package/dist/openapi/models/OperationDefinition.js +1 -1
  153. package/dist/openapi/models/OperationDownload.d.ts +29 -1
  154. package/dist/openapi/models/OperationDownload.js +5 -1
  155. package/dist/openapi/models/OperationInputModel.d.ts +4 -3
  156. package/dist/openapi/models/OperationInputModel.js +4 -3
  157. package/dist/openapi/models/OperationResponse.d.ts +20 -1
  158. package/dist/openapi/models/OperationResponse.js +5 -1
  159. package/dist/openapi/models/OperationResult.d.ts +1 -1
  160. package/dist/openapi/models/OperationResult.js +1 -1
  161. package/dist/openapi/models/OperationResultMetrics.d.ts +1 -1
  162. package/dist/openapi/models/OperationResultMetrics.js +1 -1
  163. package/dist/openapi/models/OperationSchemaDefinition.d.ts +23 -2
  164. package/dist/openapi/models/OperationSchemaDefinition.js +4 -1
  165. package/dist/openapi/models/OperationStatus.d.ts +1 -1
  166. package/dist/openapi/models/OperationStatus.js +1 -1
  167. package/dist/openapi/models/OperationType.d.ts +10 -2
  168. package/dist/openapi/models/OperationType.js +11 -3
  169. package/dist/openapi/models/OperationsSchemaResponse.d.ts +1 -1
  170. package/dist/openapi/models/OperationsSchemaResponse.js +1 -1
  171. package/dist/openapi/models/OptionSchema.d.ts +1 -1
  172. package/dist/openapi/models/OptionSchema.js +1 -1
  173. package/dist/openapi/models/PerRoleCardinalityEntry.d.ts +47 -0
  174. package/dist/openapi/models/PerRoleCardinalityEntry.js +47 -0
  175. package/dist/openapi/models/PerValueAvailabilityEntry.d.ts +1 -1
  176. package/dist/openapi/models/PerValueAvailabilityEntry.js +1 -1
  177. package/dist/openapi/models/PresignedUrlPart.d.ts +1 -1
  178. package/dist/openapi/models/PresignedUrlPart.js +1 -1
  179. package/dist/openapi/models/ProbePendingResponse.d.ts +171 -0
  180. package/dist/openapi/models/ProbePendingResponse.js +75 -0
  181. package/dist/openapi/models/ProcessingClass.d.ts +1 -1
  182. package/dist/openapi/models/ProcessingClass.js +1 -1
  183. package/dist/openapi/models/ProcessingClassBandViolation.d.ts +149 -0
  184. package/dist/openapi/models/ProcessingClassBandViolation.js +81 -0
  185. package/dist/openapi/models/ProcessingClassExceedsBandResponse.d.ts +174 -0
  186. package/dist/openapi/models/ProcessingClassExceedsBandResponse.js +76 -0
  187. package/dist/openapi/models/ProcessingClassHint.d.ts +1 -1
  188. package/dist/openapi/models/ProcessingClassHint.js +1 -1
  189. package/dist/openapi/models/ProcessingClassReason.d.ts +1 -1
  190. package/dist/openapi/models/ProcessingClassReason.js +1 -1
  191. package/dist/openapi/models/ProcessingClassRejectReason.d.ts +53 -0
  192. package/dist/openapi/models/ProcessingClassRejectReason.js +71 -0
  193. package/dist/openapi/models/ProcessingPlan.d.ts +1 -1
  194. package/dist/openapi/models/ProcessingPlan.js +1 -1
  195. package/dist/openapi/models/ProcessingPlanJob.d.ts +1 -1
  196. package/dist/openapi/models/ProcessingPlanJob.js +1 -1
  197. package/dist/openapi/models/ReadinessResponse.d.ts +1 -1
  198. package/dist/openapi/models/ReadinessResponse.js +1 -1
  199. package/dist/openapi/models/ResponseEnvelope.d.ts +1 -1
  200. package/dist/openapi/models/ResponseEnvelope.js +1 -1
  201. package/dist/openapi/models/RetryResponse.d.ts +1 -1
  202. package/dist/openapi/models/RetryResponse.js +1 -1
  203. package/dist/openapi/models/RetrySuccessEnvelope.d.ts +1 -1
  204. package/dist/openapi/models/RetrySuccessEnvelope.js +1 -1
  205. package/dist/openapi/models/SseEventType.d.ts +1 -1
  206. package/dist/openapi/models/SseEventType.js +1 -1
  207. package/dist/openapi/models/SseJobCompletedData.d.ts +1 -1
  208. package/dist/openapi/models/SseJobCompletedData.js +1 -1
  209. package/dist/openapi/models/SseJobFailedData.d.ts +1 -1
  210. package/dist/openapi/models/SseJobFailedData.js +1 -1
  211. package/dist/openapi/models/SseMultiOutputCompletion.d.ts +60 -0
  212. package/dist/openapi/models/SseMultiOutputCompletion.js +51 -0
  213. package/dist/openapi/models/SseMultiOutputCompletionMetrics.d.ts +38 -0
  214. package/dist/openapi/models/SseMultiOutputCompletionMetrics.js +43 -0
  215. package/dist/openapi/models/SseMultiOutputCompletionWithKind.d.ts +75 -0
  216. package/dist/openapi/models/SseMultiOutputCompletionWithKind.js +61 -0
  217. package/dist/openapi/models/SseMultiOutputResultEntry.d.ts +74 -0
  218. package/dist/openapi/models/SseMultiOutputResultEntry.js +51 -0
  219. package/dist/openapi/models/SseOperationCompletedData.d.ts +4 -4
  220. package/dist/openapi/models/SseOperationCompletedData.js +4 -4
  221. package/dist/openapi/models/SseOperationCompletionResult.d.ts +60 -0
  222. package/dist/openapi/models/SseOperationCompletionResult.js +47 -0
  223. package/dist/openapi/models/SseOperationFailedData.d.ts +1 -1
  224. package/dist/openapi/models/SseOperationFailedData.js +1 -1
  225. package/dist/openapi/models/SseOperationProgressData.d.ts +1 -1
  226. package/dist/openapi/models/SseOperationProgressData.js +1 -1
  227. package/dist/openapi/models/SseSingleOutputCompletion.d.ts +72 -0
  228. package/dist/openapi/models/SseSingleOutputCompletion.js +62 -0
  229. package/dist/openapi/models/SseWorkflowTerminalData.d.ts +1 -1
  230. package/dist/openapi/models/SseWorkflowTerminalData.js +1 -1
  231. package/dist/openapi/models/TierRestrictionKind.d.ts +1 -1
  232. package/dist/openapi/models/TierRestrictionKind.js +1 -1
  233. package/dist/openapi/models/TierRestrictionResponse.d.ts +20 -1
  234. package/dist/openapi/models/TierRestrictionResponse.js +1 -1
  235. package/dist/openapi/models/UploadConstraintsApplied.d.ts +1 -1
  236. package/dist/openapi/models/UploadConstraintsApplied.js +1 -1
  237. package/dist/openapi/models/UploadDurationExceedsTierResponse.d.ts +20 -1
  238. package/dist/openapi/models/UploadDurationExceedsTierResponse.js +1 -1
  239. package/dist/openapi/models/UploadFile403Response.d.ts +1 -1
  240. package/dist/openapi/models/UploadFile403Response.js +1 -1
  241. package/dist/openapi/models/UploadFile422Response.d.ts +1 -1
  242. package/dist/openapi/models/UploadFile422Response.js +1 -1
  243. package/dist/openapi/models/UploadProbeMediaMetadata.d.ts +1 -1
  244. package/dist/openapi/models/UploadProbeMediaMetadata.js +1 -1
  245. package/dist/openapi/models/UploadProbeProcessingClass.d.ts +1 -1
  246. package/dist/openapi/models/UploadProbeProcessingClass.js +1 -1
  247. package/dist/openapi/models/UploadProbeResponse.d.ts +4 -2
  248. package/dist/openapi/models/UploadProbeResponse.js +1 -1
  249. package/dist/openapi/models/UploadProbeStatus.d.ts +1 -1
  250. package/dist/openapi/models/UploadProbeStatus.js +1 -1
  251. package/dist/openapi/models/UploadProbeSuccessEnvelope.d.ts +56 -0
  252. package/dist/openapi/models/UploadProbeSuccessEnvelope.js +54 -0
  253. package/dist/openapi/models/UploadResponse.d.ts +1 -1
  254. package/dist/openapi/models/UploadResponse.js +1 -1
  255. package/dist/openapi/models/UploadSizeExceedsTierResponse.d.ts +20 -1
  256. package/dist/openapi/models/UploadSizeExceedsTierResponse.js +1 -1
  257. package/dist/openapi/models/UploadSource.d.ts +1 -1
  258. package/dist/openapi/models/UploadSource.js +1 -1
  259. package/dist/openapi/models/UploadSuccessEnvelope.d.ts +1 -1
  260. package/dist/openapi/models/UploadSuccessEnvelope.js +1 -1
  261. package/dist/openapi/models/UploadThresholds.d.ts +11 -4
  262. package/dist/openapi/models/UploadThresholds.js +2 -2
  263. package/dist/openapi/models/UserTier.d.ts +1 -1
  264. package/dist/openapi/models/UserTier.js +1 -1
  265. package/dist/openapi/models/ValidationErrorEnvelope.d.ts +1 -1
  266. package/dist/openapi/models/ValidationErrorEnvelope.js +1 -1
  267. package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.d.ts +1 -1
  268. package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.js +1 -1
  269. package/dist/openapi/models/WarningType.d.ts +1 -1
  270. package/dist/openapi/models/WarningType.js +1 -1
  271. package/dist/openapi/models/WebhookOperationContext.d.ts +1 -1
  272. package/dist/openapi/models/WebhookOperationContext.js +1 -1
  273. package/dist/openapi/models/WebhookPayload.d.ts +1 -1
  274. package/dist/openapi/models/WebhookPayload.js +1 -1
  275. package/dist/openapi/models/WorkflowCancelBillingEffect.d.ts +1 -1
  276. package/dist/openapi/models/WorkflowCancelBillingEffect.js +1 -1
  277. package/dist/openapi/models/WorkflowCancelResponse.d.ts +1 -1
  278. package/dist/openapi/models/WorkflowCancelResponse.js +1 -1
  279. package/dist/openapi/models/WorkflowCancelSuccessEnvelope.d.ts +1 -1
  280. package/dist/openapi/models/WorkflowCancelSuccessEnvelope.js +1 -1
  281. package/dist/openapi/models/WorkflowCreateRequest.d.ts +1 -1
  282. package/dist/openapi/models/WorkflowCreateRequest.js +1 -1
  283. package/dist/openapi/models/WorkflowCreateResponse.d.ts +1 -1
  284. package/dist/openapi/models/WorkflowCreateResponse.js +1 -1
  285. package/dist/openapi/models/WorkflowCreateSuccessEnvelope.d.ts +1 -1
  286. package/dist/openapi/models/WorkflowCreateSuccessEnvelope.js +1 -1
  287. package/dist/openapi/models/WorkflowDownloadResponse.d.ts +1 -1
  288. package/dist/openapi/models/WorkflowDownloadResponse.js +1 -1
  289. package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.d.ts +1 -1
  290. package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.js +1 -1
  291. package/dist/openapi/models/WorkflowEdge.d.ts +1 -1
  292. package/dist/openapi/models/WorkflowEdge.js +1 -1
  293. package/dist/openapi/models/WorkflowExpiredResponse.d.ts +20 -1
  294. package/dist/openapi/models/WorkflowExpiredResponse.js +1 -1
  295. package/dist/openapi/models/WorkflowPauseRequiredAction.d.ts +1 -1
  296. package/dist/openapi/models/WorkflowPauseRequiredAction.js +1 -1
  297. package/dist/openapi/models/WorkflowPausedDetail.d.ts +1 -1
  298. package/dist/openapi/models/WorkflowPausedDetail.js +1 -1
  299. package/dist/openapi/models/WorkflowPausedDetailLinks.d.ts +1 -1
  300. package/dist/openapi/models/WorkflowPausedDetailLinks.js +1 -1
  301. package/dist/openapi/models/WorkflowProcessing.d.ts +1 -1
  302. package/dist/openapi/models/WorkflowProcessing.js +1 -1
  303. package/dist/openapi/models/WorkflowResumeResponse.d.ts +1 -1
  304. package/dist/openapi/models/WorkflowResumeResponse.js +1 -1
  305. package/dist/openapi/models/WorkflowResumeSuccessEnvelope.d.ts +1 -1
  306. package/dist/openapi/models/WorkflowResumeSuccessEnvelope.js +1 -1
  307. package/dist/openapi/models/WorkflowSource.d.ts +1 -1
  308. package/dist/openapi/models/WorkflowSource.js +1 -1
  309. package/dist/openapi/models/WorkflowStatus.d.ts +1 -1
  310. package/dist/openapi/models/WorkflowStatus.js +1 -1
  311. package/dist/openapi/models/WorkflowStatusResponse.d.ts +1 -1
  312. package/dist/openapi/models/WorkflowStatusResponse.js +1 -1
  313. package/dist/openapi/models/WorkflowStatusSuccessEnvelope.d.ts +1 -1
  314. package/dist/openapi/models/WorkflowStatusSuccessEnvelope.js +1 -1
  315. package/dist/openapi/models/WorkflowWarning.d.ts +1 -1
  316. package/dist/openapi/models/WorkflowWarning.js +1 -1
  317. package/dist/openapi/models/WorkflowWarningSeverity.d.ts +1 -1
  318. package/dist/openapi/models/WorkflowWarningSeverity.js +1 -1
  319. package/dist/openapi/models/index.d.ts +21 -0
  320. package/dist/openapi/models/index.js +21 -0
  321. package/dist/openapi/runtime.d.ts +1 -1
  322. package/dist/openapi/runtime.js +1 -1
  323. package/dist/operations/audio_overlay.metadata.js +4 -4
  324. package/dist/operations/audio_to_video.d.ts +20 -0
  325. package/dist/operations/audio_to_video.js +17 -0
  326. package/dist/operations/audio_to_video.metadata.d.ts +2 -0
  327. package/dist/operations/audio_to_video.metadata.js +29 -0
  328. package/dist/operations/audio_watermark.metadata.js +4 -4
  329. package/dist/operations/compress.metadata.js +3 -3
  330. package/dist/operations/convert.metadata.js +2 -2
  331. package/dist/operations/custom_luma.metadata.js +2 -2
  332. package/dist/operations/image_watermark.d.ts +0 -18
  333. package/dist/operations/image_watermark.js +0 -12
  334. package/dist/operations/image_watermark.metadata.js +0 -41
  335. package/dist/operations/index.d.ts +8 -0
  336. package/dist/operations/index.js +8 -0
  337. package/dist/operations/merge.metadata.js +3 -3
  338. package/dist/operations/metadata-types.d.ts +17 -0
  339. package/dist/operations/split.d.ts +50 -0
  340. package/dist/operations/split.js +34 -0
  341. package/dist/operations/split.metadata.d.ts +2 -0
  342. package/dist/operations/split.metadata.js +103 -0
  343. package/dist/operations/thumbnail.metadata.js +1 -1
  344. package/dist/operations/video_text_watermark.d.ts +31 -0
  345. package/dist/operations/video_text_watermark.js +22 -0
  346. package/dist/operations/video_text_watermark.metadata.d.ts +2 -0
  347. package/dist/operations/video_text_watermark.metadata.js +65 -0
  348. package/dist/operations/video_watermark.d.ts +18 -0
  349. package/dist/operations/video_watermark.js +13 -0
  350. package/dist/operations/video_watermark.metadata.d.ts +2 -0
  351. package/dist/operations/video_watermark.metadata.js +52 -0
  352. package/openapi/api.yaml +1543 -121
  353. package/operations/schemas/audio_overlay.yaml +18 -6
  354. package/operations/schemas/audio_to_video.yaml +122 -0
  355. package/operations/schemas/audio_watermark.yaml +14 -6
  356. package/operations/schemas/compress.yaml +10 -4
  357. package/operations/schemas/convert.yaml +10 -3
  358. package/operations/schemas/custom_luma.yaml +10 -3
  359. package/operations/schemas/image_watermark.yaml +15 -96
  360. package/operations/schemas/merge.yaml +10 -4
  361. package/operations/schemas/split.yaml +309 -0
  362. package/operations/schemas/thumbnail.yaml +16 -3
  363. package/operations/schemas/video_text_watermark.yaml +174 -0
  364. package/operations/schemas/video_watermark.yaml +136 -0
  365. package/package.json +8 -3
  366. package/dist/asyncapi/AnonymousSchema_13.d.ts +0 -6
  367. package/dist/asyncapi/AnonymousSchema_13.js +0 -7
  368. package/dist/asyncapi/AnonymousSchema_152.d.ts +0 -5
  369. package/dist/asyncapi/AnonymousSchema_152.js +0 -6
  370. package/dist/openapi/models/ExportConfig.d.ts +0 -63
  371. package/dist/openapi/models/ExportConfig.js +0 -59
  372. package/dist/openapi/models/JobInput.d.ts +0 -53
  373. package/dist/openapi/models/JobInput.js +0 -47
  374. package/dist/openapi/models/JobSource.d.ts +0 -43
  375. package/dist/openapi/models/JobSource.js +0 -45
  376. package/dist/operations/watermark.d.ts +0 -28
  377. package/dist/operations/watermark.js +0 -19
package/openapi/api.yaml CHANGED
@@ -75,7 +75,7 @@ info:
75
75
  of truth instead of hardcoding magic numbers. A runtime
76
76
  `GET /api/uploads/limits` endpoint for dynamic discovery
77
77
  (per-tier / per-environment overrides) is a deferred follow-up.
78
- version: 2.7.1
78
+ version: 2.15.3
79
79
  contact:
80
80
  name: API Support
81
81
 
@@ -302,7 +302,7 @@ paths:
302
302
  for the remaining parts. The client uploads parts 2-N directly to S3.
303
303
 
304
304
  **Recommended client behaviour:** chunk size
305
- `UploadThresholds.multipart_chunk_size` (5 MiB) with
305
+ `UploadThresholds.multipart_chunk_size` (16 MiB) with
306
306
  `UploadThresholds.multipart_concurrency_default` parallel uploads.
307
307
  The server returns `recommended_chunk_size` per file based on
308
308
  first-chunk throughput; SDKs SHOULD prefer that runtime value when
@@ -311,9 +311,10 @@ paths:
311
311
  **Chunk sizing strategy:**
312
312
  - First chunk: fixed `UploadThresholds.multipart_first_chunk_size` = 8 MiB (sent to API)
313
313
  - Remaining chunks: API calculates `recommended_chunk_size` from throughput * 5s,
314
- clamped to 5MB-100MB
315
- - The last part may be smaller than 5MB (S3 allows this for the final part)
316
- - Maximum 500 parts per upload
314
+ clamped to 16 MiB–100 MiB
315
+ - The last part may be smaller than 16 MiB (S3 allows this for the final part)
316
+ - Maximum 10,000 parts per upload (the S3 multipart hard limit;
317
+ raised from 500 by CON-1/ADR-0011 for the 120 GB Enterprise envelope)
317
318
 
318
319
  **Pre-signed URL TTL:**
319
320
  Dynamic based on estimated upload duration * 2, clamped between 900s and 3600s.
@@ -343,7 +344,7 @@ paths:
343
344
  first_chunk_etag: '"d8e8fca2dc0f896fd7cb4cb0031ba249"'
344
345
  first_chunk_size_bytes: 8388608
345
346
  total_parts: 5
346
- recommended_chunk_size: 10485760
347
+ recommended_chunk_size: 16777216
347
348
  presigned_urls:
348
349
  - part_number: 2
349
350
  url: "https://gisl-stg-euw1-input.s3.eu-west-1.amazonaws.com/uploads/...?X-Amz-Expires=1800&..."
@@ -444,6 +445,15 @@ paths:
444
445
 
445
446
  The API calls S3 CompleteMultipartUpload and persists the upload record.
446
447
  No job or workflow is created — use `POST /api/workflows` to process the file.
448
+
449
+ **Ownership enforcement** (per session-resume work, ticket
450
+ [`HxUmVr3Y`](https://trello.com/c/HxUmVr3Y)): when
451
+ `manifest.userId` is non-null, `multipart/complete` refuses
452
+ completion by any caller other than the original authenticator
453
+ (returns 403 `MULTIPART_SESSION_OWNERSHIP`). The sister
454
+ endpoints `/status`, `/presign`, `/keepalive` apply the same
455
+ rule and additionally refuse anonymous-initiated sessions
456
+ outright (returning 403 `MULTIPART_SESSION_AUTH_REQUIRED`).
447
457
  operationId: multipartComplete
448
458
  tags:
449
459
  - Upload
@@ -479,6 +489,20 @@ paths:
479
489
  example:
480
490
  success: false
481
491
  error: "Missing ETags for parts: 3, 5"
492
+ '403':
493
+ description: |
494
+ Forbidden — session's `manifest.userId` is non-null and
495
+ differs from the authenticated caller. Per ticket
496
+ [`HxUmVr3Y`](https://trello.com/c/HxUmVr3Y) ownership
497
+ enforcement (carried by the same controller path as the
498
+ three new resume endpoints).
499
+ content:
500
+ application/json:
501
+ schema:
502
+ $ref: '#/components/schemas/ErrorEnvelope'
503
+ example:
504
+ success: false
505
+ error: "MULTIPART_SESSION_OWNERSHIP"
482
506
  '404':
483
507
  description: Upload session not found or expired
484
508
  content:
@@ -495,6 +519,341 @@ paths:
495
519
  schema:
496
520
  $ref: '#/components/schemas/ErrorEnvelope'
497
521
 
522
+ /api/uploads/multipart/{uploadId}/status:
523
+ get:
524
+ summary: Cursor over uploaded parts of an in-flight multipart session
525
+ description: |
526
+ Returns the list of parts already received by S3 for an in-flight
527
+ multipart session. Used by SDKs to resume an interrupted upload —
528
+ the client walks the cursor pages, collects which `part_number`
529
+ slots are filled, and re-presigns / re-uploads only the missing
530
+ slots via `POST /api/uploads/multipart/{uploadId}/presign`.
531
+
532
+ The response shape is a 1:1 passthrough of the S3 ListParts API —
533
+ `next_part_number_marker` and `is_truncated` mirror the upstream
534
+ wire shape verbatim (no translation layer, zero impedance
535
+ mismatch for SDKs that already speak S3 vocabulary).
536
+
537
+ **Authentication required.** Anonymous-initiated sessions (allowed
538
+ at `/initiate` today; the `8LABloaz` follow-up flips that to
539
+ require-auth) cannot use this endpoint and receive 403
540
+ `MULTIPART_SESSION_AUTH_REQUIRED`. Authenticated callers can only
541
+ list parts of sessions they initiated (403
542
+ `MULTIPART_SESSION_OWNERSHIP` otherwise).
543
+
544
+ Cursor semantics: pass `cursor=0` (default) for the first page;
545
+ the server returns up to `limit` parts plus `next_part_number_marker`
546
+ and `is_truncated`. To walk: pass each `next_part_number_marker`
547
+ as the next request's `cursor` until `is_truncated: false`. The
548
+ marker is `null` on the final page.
549
+
550
+ Completion-readiness is client-derived (walk all pages, compare
551
+ the count of returned parts to `total_parts`).
552
+ operationId: multipartStatus
553
+ tags:
554
+ - Upload
555
+ parameters:
556
+ - name: uploadId
557
+ in: path
558
+ required: true
559
+ description: Multipart upload session identifier from the initiate response.
560
+ schema:
561
+ $ref: '#/components/schemas/UuidV7'
562
+ - name: cursor
563
+ in: query
564
+ required: false
565
+ description: |
566
+ S3 ListParts `PartNumberMarker` (the part-number to start
567
+ listing **after**). `0` (default) returns from the first
568
+ part. Walk by passing each response's
569
+ `next_part_number_marker` until `is_truncated: false`.
570
+ schema:
571
+ type: integer
572
+ minimum: 0
573
+ maximum: 9999
574
+ default: 0
575
+ - name: limit
576
+ in: query
577
+ required: false
578
+ description: |
579
+ Page size — maximum number of parts returned per request.
580
+ `1000` is the default and the upstream S3 `MaxParts` cap.
581
+ schema:
582
+ type: integer
583
+ minimum: 1
584
+ maximum: 1000
585
+ default: 1000
586
+ responses:
587
+ '200':
588
+ description: Page of uploaded parts.
589
+ content:
590
+ application/json:
591
+ schema:
592
+ $ref: '#/components/schemas/MultipartStatusSuccessEnvelope'
593
+ example:
594
+ success: true
595
+ data:
596
+ upload_id: "019539ab-1111-7000-8000-000000000001"
597
+ multipart_upload_id: "5MqcMpgg9_pKVdoO9.X9eK0w2g3Sq5_h"
598
+ cloud_key: "uploads/01/019539ab-1111-7000-8000-000000000001/source"
599
+ total_parts: 5
600
+ uploaded_parts:
601
+ - part_number: 1
602
+ etag: '"d8e8fca2dc0f896fd7cb4cb0031ba249"'
603
+ size_bytes: 8388608
604
+ last_modified: "2026-05-20T13:30:00.000Z"
605
+ - part_number: 2
606
+ etag: '"7c3e2c4b6e3e7e0e8f9d1a2b3c4d5e6f"'
607
+ size_bytes: 16777216
608
+ last_modified: "2026-05-20T13:31:14.000Z"
609
+ next_part_number_marker: null
610
+ is_truncated: false
611
+ manifest_expires_at: "2026-05-22T13:31:14.000Z"
612
+ recommended_chunk_size: 16777216
613
+ '401':
614
+ description: Authentication required.
615
+ content:
616
+ application/json:
617
+ schema:
618
+ $ref: '#/components/schemas/ErrorEnvelope'
619
+ '403':
620
+ description: |
621
+ Forbidden — either the session was anonymously initiated
622
+ (`MULTIPART_SESSION_AUTH_REQUIRED`) or the authenticated
623
+ caller is not the session owner (`MULTIPART_SESSION_OWNERSHIP`).
624
+ content:
625
+ application/json:
626
+ schema:
627
+ $ref: '#/components/schemas/ErrorEnvelope'
628
+ examples:
629
+ auth_required:
630
+ summary: Anonymous-initiated session
631
+ value:
632
+ success: false
633
+ error: "MULTIPART_SESSION_AUTH_REQUIRED"
634
+ ownership:
635
+ summary: Session belongs to a different authenticated caller
636
+ value:
637
+ success: false
638
+ error: "MULTIPART_SESSION_OWNERSHIP"
639
+ '404':
640
+ description: Multipart session not found or expired.
641
+ content:
642
+ application/json:
643
+ schema:
644
+ $ref: '#/components/schemas/ErrorEnvelope'
645
+ example:
646
+ success: false
647
+ error: "MULTIPART_SESSION_NOT_FOUND"
648
+ '500':
649
+ description: Internal server error
650
+ content:
651
+ application/json:
652
+ schema:
653
+ $ref: '#/components/schemas/ErrorEnvelope'
654
+
655
+ /api/uploads/multipart/{uploadId}/presign:
656
+ post:
657
+ summary: Sparse re-presign of multipart parts (resume / retry)
658
+ description: |
659
+ Returns fresh pre-signed PUT URLs for a caller-supplied subset
660
+ of part numbers. Used by SDKs to (a) resume an interrupted
661
+ upload after the original presigned URLs have expired, and
662
+ (b) retry individual failed PUTs without re-presigning the
663
+ whole upload.
664
+
665
+ Part numbers MUST satisfy `2 ≤ n ≤ total_parts`. **Part 1 is
666
+ rejected** because the manifest stores its ETag from `/initiate`
667
+ — re-presigning part 1 would break the eventual
668
+ `multipart/complete` call. Request the list with
669
+ `uniqueItems`; duplicates are rejected with a 400.
670
+
671
+ At most 100 part numbers per request. SDKs paginate larger
672
+ sets across multiple `/presign` calls.
673
+
674
+ **Authentication required.** Same `MULTIPART_SESSION_*` codes
675
+ apply as on `/status`.
676
+ operationId: multipartPresign
677
+ tags:
678
+ - Upload
679
+ parameters:
680
+ - name: uploadId
681
+ in: path
682
+ required: true
683
+ description: Multipart upload session identifier from the initiate response.
684
+ schema:
685
+ $ref: '#/components/schemas/UuidV7'
686
+ requestBody:
687
+ required: true
688
+ content:
689
+ application/json:
690
+ schema:
691
+ $ref: '#/components/schemas/MultipartPresignRequest'
692
+ responses:
693
+ '200':
694
+ description: Pre-signed URLs returned for the requested parts.
695
+ content:
696
+ application/json:
697
+ schema:
698
+ $ref: '#/components/schemas/MultipartPresignSuccessEnvelope'
699
+ example:
700
+ success: true
701
+ data:
702
+ upload_id: "019539ab-1111-7000-8000-000000000001"
703
+ presigned_urls:
704
+ - part_number: 3
705
+ url: "https://gisl-stg-euw1-input.s3.eu-west-1.amazonaws.com/uploads/...?X-Amz-Expires=1800&..."
706
+ expires_at: "2026-05-20T14:01:14.000Z"
707
+ - part_number: 5
708
+ url: "https://gisl-stg-euw1-input.s3.eu-west-1.amazonaws.com/uploads/...?X-Amz-Expires=1800&..."
709
+ expires_at: "2026-05-20T14:01:14.000Z"
710
+ '400':
711
+ description: |
712
+ Validation failed — e.g. body too large, malformed JSON,
713
+ `part_numbers` empty / >100, contains duplicates, contains
714
+ `1`, contains values outside `[2, total_parts]`.
715
+ content:
716
+ application/json:
717
+ schema:
718
+ $ref: '#/components/schemas/ErrorEnvelope'
719
+ example:
720
+ success: false
721
+ error: "part_numbers must contain unique values in [2, total_parts]; part 1 is sealed"
722
+ '401':
723
+ description: Authentication required.
724
+ content:
725
+ application/json:
726
+ schema:
727
+ $ref: '#/components/schemas/ErrorEnvelope'
728
+ '403':
729
+ description: |
730
+ Forbidden — either the session was anonymously initiated
731
+ (`MULTIPART_SESSION_AUTH_REQUIRED`) or the authenticated
732
+ caller is not the session owner (`MULTIPART_SESSION_OWNERSHIP`).
733
+ content:
734
+ application/json:
735
+ schema:
736
+ $ref: '#/components/schemas/ErrorEnvelope'
737
+ examples:
738
+ auth_required:
739
+ value:
740
+ success: false
741
+ error: "MULTIPART_SESSION_AUTH_REQUIRED"
742
+ ownership:
743
+ value:
744
+ success: false
745
+ error: "MULTIPART_SESSION_OWNERSHIP"
746
+ '404':
747
+ description: Multipart session not found or expired.
748
+ content:
749
+ application/json:
750
+ schema:
751
+ $ref: '#/components/schemas/ErrorEnvelope'
752
+ example:
753
+ success: false
754
+ error: "MULTIPART_SESSION_NOT_FOUND"
755
+ '422':
756
+ description: |
757
+ Session no longer accepts re-presign requests because the
758
+ assembled object would exceed the S3 multipart capacity
759
+ cap. Pre-S3 server-side capacity gate; distinct from
760
+ tier-quota rejections at `/initiate` time.
761
+ content:
762
+ application/json:
763
+ schema:
764
+ $ref: '#/components/schemas/ErrorEnvelope'
765
+ example:
766
+ success: false
767
+ error: "FILE_TOO_LARGE_FOR_MULTIPART"
768
+ '500':
769
+ description: Internal server error
770
+ content:
771
+ application/json:
772
+ schema:
773
+ $ref: '#/components/schemas/ErrorEnvelope'
774
+
775
+ /api/uploads/multipart/{uploadId}/keepalive:
776
+ post:
777
+ summary: Refresh the multipart session manifest TTL
778
+ description: |
779
+ Resets the session manifest's TTL to 48 h ahead of now. The
780
+ server uses an atomic Redis `EXPIRE`; the operation is
781
+ idempotent — calling it multiple times in succession produces
782
+ the same effective TTL window.
783
+
784
+ SDKs SHOULD call this periodically on resume flows (e.g. once
785
+ per minute while a resumable upload is paused) to prevent the
786
+ manifest from lapsing. The manifest TTL is decoupled from
787
+ individual presigned-URL TTLs (which are typically 900s–3600s);
788
+ re-presigning expired URLs requires the manifest still be
789
+ alive.
790
+
791
+ Empty request body.
792
+
793
+ **Authentication required.** Same `MULTIPART_SESSION_*` codes
794
+ apply as on `/status` and `/presign`.
795
+ operationId: multipartKeepalive
796
+ tags:
797
+ - Upload
798
+ parameters:
799
+ - name: uploadId
800
+ in: path
801
+ required: true
802
+ description: Multipart upload session identifier from the initiate response.
803
+ schema:
804
+ $ref: '#/components/schemas/UuidV7'
805
+ responses:
806
+ '200':
807
+ description: Manifest TTL refreshed; new expiry returned.
808
+ content:
809
+ application/json:
810
+ schema:
811
+ $ref: '#/components/schemas/MultipartKeepaliveSuccessEnvelope'
812
+ example:
813
+ success: true
814
+ data:
815
+ upload_id: "019539ab-1111-7000-8000-000000000001"
816
+ manifest_expires_at: "2026-05-22T13:31:14.000Z"
817
+ '401':
818
+ description: Authentication required.
819
+ content:
820
+ application/json:
821
+ schema:
822
+ $ref: '#/components/schemas/ErrorEnvelope'
823
+ '403':
824
+ description: |
825
+ Forbidden — either the session was anonymously initiated
826
+ (`MULTIPART_SESSION_AUTH_REQUIRED`) or the authenticated
827
+ caller is not the session owner (`MULTIPART_SESSION_OWNERSHIP`).
828
+ content:
829
+ application/json:
830
+ schema:
831
+ $ref: '#/components/schemas/ErrorEnvelope'
832
+ examples:
833
+ auth_required:
834
+ value:
835
+ success: false
836
+ error: "MULTIPART_SESSION_AUTH_REQUIRED"
837
+ ownership:
838
+ value:
839
+ success: false
840
+ error: "MULTIPART_SESSION_OWNERSHIP"
841
+ '404':
842
+ description: Multipart session not found or expired.
843
+ content:
844
+ application/json:
845
+ schema:
846
+ $ref: '#/components/schemas/ErrorEnvelope'
847
+ example:
848
+ success: false
849
+ error: "MULTIPART_SESSION_NOT_FOUND"
850
+ '500':
851
+ description: Internal server error
852
+ content:
853
+ application/json:
854
+ schema:
855
+ $ref: '#/components/schemas/ErrorEnvelope'
856
+
498
857
  /api/uploads/{id}/metadata:
499
858
  get:
500
859
  summary: Get file metadata
@@ -609,82 +968,92 @@ paths:
609
968
  responses:
610
969
  '200':
611
970
  description: |
612
- Probe completed. The `probe_status` field indicates
971
+ Probe completed. The `data.probe_status` field indicates
613
972
  whether the file is workflow-ready (`ok`) or has issues
614
973
  that warrant exclusion / re-upload.
615
974
  content:
616
975
  application/json:
617
976
  schema:
618
- $ref: '#/components/schemas/UploadProbeResponse'
977
+ $ref: '#/components/schemas/UploadProbeSuccessEnvelope'
619
978
  examples:
620
979
  ok_short_form:
621
980
  summary: Short-form clip probed cleanly
622
981
  value:
623
- file_id: "019539ab-1111-7000-8000-000000000001"
624
- probe_status: "ok"
625
- media_metadata:
626
- duration_seconds: 187
627
- width: 1920
628
- height: 1080
629
- codec: "h264"
630
- container: "mp4"
631
- audio_layout: "stereo"
632
- probed_at: "2026-04-26T13:50:00Z"
633
- processing_class_pre_assignment: "short_form"
982
+ success: true
983
+ data:
984
+ file_id: "019539ab-1111-7000-8000-000000000001"
985
+ probe_status: "ok"
986
+ media_metadata:
987
+ duration_seconds: 187
988
+ width: 1920
989
+ height: 1080
990
+ codec: "h264"
991
+ container: "mp4"
992
+ audio_layout: "stereo"
993
+ probed_at: "2026-04-26T13:50:00Z"
994
+ processing_class_pre_assignment: "short_form"
634
995
  ok_long_form:
635
996
  summary: Long-form clip — full M2 union populated (video + audio fields)
636
997
  value:
637
- file_id: "019539ab-1111-7000-8000-000000000002"
638
- probe_status: "ok"
639
- media_metadata:
640
- duration_seconds: 5400
641
- width: 3840
642
- height: 2160
643
- codec: "h265"
644
- audio_codec: "aac"
645
- container: "mp4"
646
- fps: 23.976
647
- bitrate_bps: 18000000
648
- audio_layout: "stereo"
649
- channels: 2
650
- sample_rate_hz: 48000
651
- probed_at: "2026-04-26T13:50:00Z"
652
- processing_class_pre_assignment: "long_form"
998
+ success: true
999
+ data:
1000
+ file_id: "019539ab-1111-7000-8000-000000000002"
1001
+ probe_status: "ok"
1002
+ media_metadata:
1003
+ duration_seconds: 5400
1004
+ width: 3840
1005
+ height: 2160
1006
+ codec: "h265"
1007
+ audio_codec: "aac"
1008
+ container: "mp4"
1009
+ fps: 23.976
1010
+ bitrate_bps: 18000000
1011
+ audio_layout: "stereo"
1012
+ channels: 2
1013
+ sample_rate_hz: 48000
1014
+ probed_at: "2026-04-26T13:50:00Z"
1015
+ processing_class_pre_assignment: "long_form"
653
1016
  corrupt:
654
1017
  summary: File header damaged — caller should exclude
655
1018
  value:
656
- file_id: "019539ab-1111-7000-8000-000000000003"
657
- probe_status: "corrupt"
658
- media_metadata:
659
- probed_at: "2026-04-26T13:50:00Z"
660
- processing_class_pre_assignment: "blocked"
1019
+ success: true
1020
+ data:
1021
+ file_id: "019539ab-1111-7000-8000-000000000003"
1022
+ probe_status: "corrupt"
1023
+ media_metadata:
1024
+ probed_at: "2026-04-26T13:50:00Z"
1025
+ processing_class_pre_assignment: "blocked"
661
1026
  unsupported_codec:
662
1027
  summary: Container readable but codec unsupported
663
1028
  value:
664
- file_id: "019539ab-1111-7000-8000-000000000004"
665
- probe_status: "unsupported_codec"
666
- media_metadata:
667
- duration_seconds: 600
668
- width: 1280
669
- height: 720
670
- codec: "prores_4444"
671
- container: "mov"
672
- probed_at: "2026-04-26T13:50:00Z"
673
- processing_class_pre_assignment: "blocked"
1029
+ success: true
1030
+ data:
1031
+ file_id: "019539ab-1111-7000-8000-000000000004"
1032
+ probe_status: "unsupported_codec"
1033
+ media_metadata:
1034
+ duration_seconds: 600
1035
+ width: 1280
1036
+ height: 720
1037
+ codec: "prores_4444"
1038
+ container: "mov"
1039
+ probed_at: "2026-04-26T13:50:00Z"
1040
+ processing_class_pre_assignment: "blocked"
674
1041
  blocked_by_tier:
675
1042
  summary: Free-tier caller probes a long-form clip
676
1043
  value:
677
- file_id: "019539ab-1111-7000-8000-000000000005"
678
- probe_status: "ok"
679
- media_metadata:
680
- duration_seconds: 5400
681
- width: 1920
682
- height: 1080
683
- codec: "h264"
684
- container: "mp4"
685
- audio_layout: "stereo"
686
- probed_at: "2026-04-26T13:50:00Z"
687
- processing_class_pre_assignment: "blocked"
1044
+ success: true
1045
+ data:
1046
+ file_id: "019539ab-1111-7000-8000-000000000005"
1047
+ probe_status: "ok"
1048
+ media_metadata:
1049
+ duration_seconds: 5400
1050
+ width: 1920
1051
+ height: 1080
1052
+ codec: "h264"
1053
+ container: "mp4"
1054
+ audio_layout: "stereo"
1055
+ probed_at: "2026-04-26T13:50:00Z"
1056
+ processing_class_pre_assignment: "blocked"
688
1057
  '401':
689
1058
  description: Authentication required.
690
1059
  content:
@@ -762,12 +1131,21 @@ paths:
762
1131
  side-effect dependencies that don't surface as `from`
763
1132
  references).
764
1133
 
765
- **Default operation:** if `operations` is omitted on a
766
- single-input job with `source: { type: upload, ... }`, the
767
- default is `[{ "type": "compress" }]` with default options for
768
- the detected MIME type. Multi-input jobs must always specify
1134
+ **Default operation:** if `operations` is omitted or
1135
+ explicitly empty (`[]`) on a single-input job with
1136
+ `source: { type: upload, ... }`, the default is
1137
+ `[{ "type": "compress" }]` with default options for the
1138
+ detected MIME type. Multi-input jobs must always specify
769
1139
  operations explicitly.
770
1140
 
1141
+ **Compression opt-out:** V2 has no `skip_compression` flag —
1142
+ `operations[]` *is* the chain. To opt a job out of
1143
+ compression, send an explicit non-empty `operations[]` that
1144
+ does not include `compress`; on a single-input upload job an
1145
+ empty `[]` does *not* opt out (it is treated as omitted and
1146
+ still receives the implicit `compress` — see above). Per
1147
+ [ADR-0004](../docs/decisions/0004-job-shape.md).
1148
+
771
1149
  **Multi-input constraint:** multi-input jobs (`inputs[]`) must
772
1150
  have exactly one operation, which must be a multi-input type
773
1151
  (`merge`, `archive`, `image_watermark`, `custom_luma`, or
@@ -1077,7 +1455,7 @@ paths:
1077
1455
  error: "Upload not found: 019539ab-1111-7000-8000-999999999999"
1078
1456
  '422':
1079
1457
  description: |
1080
- Semantically invalid request. Either:
1458
+ Semantically invalid request. One of:
1081
1459
 
1082
1460
  - Generic validation error (unknown operation type, invalid
1083
1461
  option combinations, cyclic dependency in workflow_edges,
@@ -1090,6 +1468,22 @@ paths:
1090
1468
  with `error_type: feature_not_available`. Per ADR-0001
1091
1469
  §1.3 Tension 1 rule, the server MUST honour tagged
1092
1470
  availability.
1471
+ - Processing-class band-ceiling overflow — returned as
1472
+ `ProcessingClassExceedsBandResponse` with `error_type:
1473
+ processing_class_exceeds_band`. Fires when one or more
1474
+ jobs cannot be classified because input size/duration
1475
+ exceeds the `long_form` (or `long_form_re_encode`)
1476
+ ceiling under the caller's effective per-tier caps
1477
+ (`per_tier_constraints` overlay per
1478
+ [ADR-0011](../docs/decisions/0011-per-tier-processing-class-constraints.md)).
1479
+ Per-violation `required_tier` names the tier (if any)
1480
+ whose `per_tier_constraints` would accommodate the
1481
+ request (`null` = no tier resolves, e.g. enterprise
1482
+ already at the 120 GB hard ceiling). Distinct from
1483
+ `TierRestrictionResponse` (403, request-level upload
1484
+ quota — see [ADR-0012](../docs/decisions/0012-processing-class-band-reject-envelope.md)
1485
+ for the band-vs-tier split). Per ADR-0001 §F6
1486
+ batched-violations rule, `violations[]` is fail-all.
1093
1487
  - Re-encode required (per ticket I16-CONS, Trello
1094
1488
  `7nCZXEru`). Returned for `merge.video` jobs with
1095
1489
  `re_encode_mode: never` when the compatibility probe
@@ -1108,27 +1502,49 @@ paths:
1108
1502
  Backward-compatibility note: the prior 422 shape
1109
1503
  (`ValidationErrorEnvelope`) is preserved unchanged — its
1110
1504
  `details[]` array is the structural signature consumers
1111
- duck-type against. The new `FeatureNotAvailableResponse`
1112
- branch carries `error_type: feature_not_available` +
1113
- `violations[]` instead. The `REQUIRES_REENCODE` flavour
1114
- reuses `ValidationErrorEnvelope` (a user-fixable validation
1115
- error in the same family as `INVALID_OPTIONS`) and is
1116
- distinguished by the stable `error: "REQUIRES_REENCODE"`
1117
- string code rather than a new `error_type` enum value.
1118
- Branches are distinguishable by required fields (`details`
1119
- vs `error_type`+`violations`), so plain `oneOf` matches
1120
- without a discriminator object see `openapi/api.yaml`
1121
- §"FEATURE AVAILABILITY ENVELOPES" commentary for the
1122
- convention rationale (no discriminator here because
1123
- `ValidationErrorEnvelope` has no `error_type` field;
1124
- OpenAPI 3.1 discriminators require the property to be
1125
- present on every branch).
1505
+ duck-type against. `FeatureNotAvailableResponse` carries
1506
+ `error_type: feature_not_available` + `violations[]`;
1507
+ `ProcessingClassExceedsBandResponse` carries `error_type:
1508
+ processing_class_exceeds_band` + `violations[]`.
1509
+ `ProbePendingResponse` carries `error_type: probe_pending`
1510
+ + `job_ref` (and NO `violations[]` / `details[]`), emitted
1511
+ when the probe-pending gate is on and a job's upload has
1512
+ not yet been probed. The `REQUIRES_REENCODE` flavour reuses
1513
+ `ValidationErrorEnvelope`
1514
+ (a user-fixable validation error in the same family as
1515
+ `INVALID_OPTIONS`) and is distinguished by the stable
1516
+ `error: "REQUIRES_REENCODE"` string code rather than a new
1517
+ `error_type` enum value. Branches are distinguishable by
1518
+ required-field shape (`details` vs `error_type` value), so
1519
+ plain `oneOf` matches without a discriminator object — see
1520
+ `openapi/api.yaml` §"FEATURE AVAILABILITY ENVELOPES"
1521
+ commentary for the convention rationale (no discriminator
1522
+ here because `ValidationErrorEnvelope` has no `error_type`
1523
+ field; OpenAPI 3.1 discriminators require the property to
1524
+ be present on every branch).
1525
+ headers:
1526
+ Retry-After:
1527
+ required: false
1528
+ description: |
1529
+ Optional, best-effort. Present only on the
1530
+ `probe_pending` branch — suggested seconds to wait
1531
+ before re-polling the upload probe and retrying the
1532
+ workflow. Delta-seconds (RFC 9110), mirroring the 429
1533
+ login path's `Retry-After` convention. Absent until the
1534
+ server-side probe-staleness bound (API `e1Idv6UL`)
1535
+ lands; consumers MUST treat absence as "retry on your
1536
+ own backoff schedule."
1537
+ schema:
1538
+ type: integer
1539
+ minimum: 0
1126
1540
  content:
1127
1541
  application/json:
1128
1542
  schema:
1129
1543
  oneOf:
1130
1544
  - $ref: '#/components/schemas/ValidationErrorEnvelope'
1131
1545
  - $ref: '#/components/schemas/FeatureNotAvailableResponse'
1546
+ - $ref: '#/components/schemas/ProcessingClassExceedsBandResponse'
1547
+ - $ref: '#/components/schemas/ProbePendingResponse'
1132
1548
  examples:
1133
1549
  validation_error:
1134
1550
  summary: Generic validation error (legacy shape)
@@ -1155,6 +1571,56 @@ paths:
1155
1571
  documentation_url: "https://docs.giveitsmaller.com/operations/audio_overlay"
1156
1572
  - feature: "operation.merge.mime_group.image.option.transition.dissolve"
1157
1573
  availability: "experimental"
1574
+ probe_pending:
1575
+ summary: |
1576
+ Probe-pending gate is on and a single-op video
1577
+ compress references an upload whose server-side
1578
+ probe has not yet landed. Client polls the upload
1579
+ probe endpoint, then re-POSTs the workflow. The
1580
+ `Retry-After` header (5s here) hints the poll delay.
1581
+ value:
1582
+ success: false
1583
+ error: "Upload probe has not completed; poll the upload probe endpoint and retry"
1584
+ error_type: "probe_pending"
1585
+ job_ref: "job_0"
1586
+ message_key: "error.probe_pending"
1587
+ processing_class_exceeds_band_combined_size:
1588
+ summary: |
1589
+ merge job whose summed inputs (130 GB) exceed the
1590
+ Enterprise long_form_re_encode ceiling (120 GB
1591
+ hard cap per ADR-0011). Terminal — no tier
1592
+ resolves it; `required_tier` is null.
1593
+ value:
1594
+ success: false
1595
+ error: "Workflow contains inputs that exceed the long-form processing-class ceiling"
1596
+ error_type: "processing_class_exceeds_band"
1597
+ violations:
1598
+ - reason: "combined_size_exceeds_long_form"
1599
+ job_ref: "stitched"
1600
+ operation: "merge"
1601
+ processing_class: "long_form_re_encode"
1602
+ actual: 130000000000
1603
+ ceiling: 120000000000
1604
+ required_tier: null
1605
+ documentation_url: "https://docs.giveitsmaller.com/processing-class/long-form"
1606
+ processing_class_exceeds_band_single_input_duration:
1607
+ summary: |
1608
+ compress.video job whose single input duration
1609
+ (15 hours = 54000s) exceeds the Pro long_form
1610
+ `max_input_duration` (PT12H = 43200s). Upgrade to
1611
+ Enterprise resolves the duration cap.
1612
+ value:
1613
+ success: false
1614
+ error: "Workflow contains inputs that exceed the long-form processing-class ceiling"
1615
+ error_type: "processing_class_exceeds_band"
1616
+ violations:
1617
+ - reason: "input_duration_exceeds_long_form"
1618
+ job_ref: "job_0"
1619
+ operation: "compress"
1620
+ processing_class: "long_form"
1621
+ actual: 54000
1622
+ ceiling: 43200
1623
+ required_tier: "enterprise"
1158
1624
  requires_reencode:
1159
1625
  summary: merge.video re_encode_mode=never with incompatible inputs (I16-CONS)
1160
1626
  value:
@@ -1343,6 +1809,35 @@ paths:
1343
1809
  event: workflow.completed
1344
1810
  data: {"workflow_id":"wf-uuid","status":"completed"}
1345
1811
  ```
1812
+
1813
+ **Reconnect & delivery semantics:**
1814
+
1815
+ Delivery is **at-least-once per connection with no client-driven
1816
+ resume**. The server does **not** read an inbound `Last-Event-ID`
1817
+ header; each connection re-derives state from scratch and replays
1818
+ every underlying stream from the start. Consequently **terminal events
1819
+ (`operation.completed` / `operation.failed`, `job.completed` /
1820
+ `job.failed`, `workflow.completed` / `workflow.failed` /
1821
+ `workflow.partially_failed`) are re-delivered on every (re)connection**.
1822
+ Within a single connection events are not duplicated (the cursor
1823
+ advances as events are sent), so duplicates only arise across
1824
+ reconnects. Clients MUST therefore treat the stream as idempotent and
1825
+ dedupe terminal events themselves (e.g. by `operation_id` + event type,
1826
+ or by workflow/job terminal status) rather than relying on
1827
+ resume-from-cursor. Resume-from-`Last-Event-ID` is **not** implemented;
1828
+ if added later it will be a separate, explicitly-specified feature.
1829
+
1830
+ **`id:` field (SSE event id):** emitted **only on operation frames**
1831
+ (`operation.progress` / `operation.completed` / `operation.failed`),
1832
+ where it carries the underlying stream's event id. **Transition frames**
1833
+ (`job.completed` / `job.failed`, `workflow.*`) carry **no `id:`**.
1834
+ Because there is no `Last-Event-ID` resume, any emitted `id:` is
1835
+ informational only — clients cannot use it to resume a stream.
1836
+
1837
+ **Framing vs payload:** the `Sse*Data` component schemas describe only
1838
+ the `data:` JSON payload of each event. The SSE envelope framing itself
1839
+ (the `id:` and `event:` lines) is transport-level and intentionally
1840
+ **not** schema-described.
1346
1841
  operationId: streamWorkflowEvents
1347
1842
  tags:
1348
1843
  - Workflow
@@ -2796,6 +3291,25 @@ components:
2796
3291
  `REQUIRES_REENCODE`). Canonical English; never localised.
2797
3292
  SDKs duck-type on this field for typed error-branch
2798
3293
  helpers.
3294
+
3295
+ Multipart-session resume codes (per ticket
3296
+ [`HxUmVr3Y`](https://trello.com/c/HxUmVr3Y), V2.10.0):
3297
+ - `MULTIPART_SESSION_NOT_FOUND` (404) — upload_id does
3298
+ not match an in-flight session (expired / never
3299
+ existed / wrong account namespace). Fired by /status,
3300
+ /presign, /keepalive.
3301
+ - `MULTIPART_SESSION_OWNERSHIP` (403) — authenticated
3302
+ caller is not the session owner. Fired by /status,
3303
+ /presign, /keepalive, /complete (when manifest.userId
3304
+ is non-null and differs).
3305
+ - `MULTIPART_SESSION_AUTH_REQUIRED` (403) — session was
3306
+ anonymously initiated; the three resume endpoints
3307
+ require authentication. The `8LABloaz` follow-up will
3308
+ flip `/initiate` to also require auth.
3309
+ - `FILE_TOO_LARGE_FOR_MULTIPART` (422) — assembled object
3310
+ would exceed the S3 multipart capacity cap. Pre-S3
3311
+ server-side capacity gate; distinct from tier-quota
3312
+ rejections (`upload_size_exceeds_tier`).
2799
3313
  message:
2800
3314
  type: string
2801
3315
  description: |
@@ -3056,6 +3570,62 @@ components:
3056
3570
  additionalProperties:
3057
3571
  $ref: '#/components/schemas/PerValueAvailabilityEntry'
3058
3572
 
3573
+ PerRoleCardinality:
3574
+ type: object
3575
+ description: |
3576
+ Map of role-name → `PerRoleCardinalityEntry`. Attached to
3577
+ a role-based multi-input operation's `per_role_cardinality`
3578
+ field to declare the required and maximum input count per
3579
+ role. **Cardinality overlay, not availability overlay** —
3580
+ sits alongside `PerValueAvailability` / `PerMimeAvailability`
3581
+ structurally but expresses a different concern.
3582
+
3583
+ Keys MUST be a subset of the `JobInputRole` enum
3584
+ (CI-checked by `scripts/check-per-role-cardinality.py`).
3585
+ The script additionally enforces arithmetic-consistency:
3586
+ `sum(role.min) == OperationSchemaDefinition.min_inputs`
3587
+ and `sum(role.max) == OperationSchemaDefinition.max_inputs`
3588
+ — this is the load-bearing invariant that makes the
3589
+ primitive useful.
3590
+
3591
+ Absence of `per_role_cardinality` on a role-based op keeps
3592
+ the current prose-only semantics (image_watermark /
3593
+ audio_overlay / custom_luma encode their role rules in
3594
+ prose at `JobInputRole`'s description today; migration to
3595
+ predicate form is per-op follow-up work).
3596
+
3597
+ Per ticket [`SlluxMBN`](https://trello.com/c/SlluxMBN) /
3598
+ ADR-0015. Consumed by `OperationInputRoleValidator` in
3599
+ `compression_api` (rule table becomes data-driven) and by
3600
+ SDK generators when emitting typed role-binding helpers.
3601
+ additionalProperties:
3602
+ $ref: '#/components/schemas/PerRoleCardinalityEntry'
3603
+
3604
+ PerRoleCardinalityEntry:
3605
+ type: object
3606
+ description: |
3607
+ Per-role cardinality entry. `min: 0` makes the role optional.
3608
+ `max: 1` is the most common upper bound (one base, one
3609
+ overlay); future role-based ops may declare higher maxima
3610
+ (e.g. multi-overlay video composites). Both fields default to
3611
+ `1` when absent on a role key — but consumers SHOULD treat
3612
+ absence of either field as a contract bug surfaced by
3613
+ `scripts/check-per-role-cardinality.py` rather than silently
3614
+ defaulting.
3615
+ required:
3616
+ - min
3617
+ - max
3618
+ properties:
3619
+ min:
3620
+ type: integer
3621
+ minimum: 0
3622
+ description: Minimum input count for this role (0 = optional).
3623
+ max:
3624
+ type: integer
3625
+ minimum: 1
3626
+ description: |
3627
+ Maximum input count for this role. MUST be >= `min`.
3628
+
3059
3629
  # ============================================
3060
3630
  # EXTERNAL SOURCES + DESTINATIONS
3061
3631
  # ============================================
@@ -4274,8 +4844,12 @@ components:
4274
4844
  rejection.
4275
4845
 
4276
4846
  The 422 response is delivered alongside `ValidationErrorEnvelope`
4277
- via `oneOf + discriminator` on `error_type` — see the 422
4278
- response on `POST /api/workflows`.
4847
+ and `ProcessingClassExceedsBandResponse` via a naked `oneOf`
4848
+ (duck-typed on required-field shape — `details` vs `error_type`
4849
+ value: `feature_not_available` vs `processing_class_exceeds_band`).
4850
+ No discriminator object is used because `ValidationErrorEnvelope`
4851
+ has no `error_type` field. See the 422 response on
4852
+ `POST /api/workflows`.
4279
4853
  allOf:
4280
4854
  - $ref: '#/components/schemas/ErrorEnvelope'
4281
4855
  - type: object
@@ -4299,31 +4873,278 @@ components:
4299
4873
  items:
4300
4874
  $ref: '#/components/schemas/FeatureViolation'
4301
4875
 
4302
- FeatureTierRestrictedResponse:
4876
+ ProbePendingResponse:
4303
4877
  description: |
4304
- 403 response body when a workflow references one or more features
4305
- gated by a higher subscription tier than the caller has.
4306
-
4307
- Distinct from `TierRestrictionResponse` (also 403): this envelope
4308
- is per-feature granularity (operation / mime_group / option /
4309
- per_value); `TierRestrictionResponse` is request-level (file size
4310
- or MIME). The two envelopes are discriminated by `error_type` at
4311
- the response `oneOf` level.
4878
+ 422 response on `POST /api/workflows` when the probe-pending
4879
+ gate (API `av1J0rEF`, shipped behind a default-OFF feature flag)
4880
+ is enabled and a job references an upload whose server-side
4881
+ probe has not yet completed at workflow-create time. Rather than
4882
+ silently routing the job as `short_form` (which hard-fails long
4883
+ video clips), the server rejects with this envelope so the
4884
+ client can recover deterministically.
4885
+
4886
+ **Recovery contract.** Poll `POST /api/uploads/{id}/probe` for
4887
+ the pending upload until its `probe_status` is terminal
4888
+ (`ok` → re-`POST /api/workflows` the same request; `corrupt` /
4889
+ `unsupported_codec` → surface the probe error, do not retry).
4890
+ The `Retry-After` response header (when present) carries the
4891
+ suggested delay in seconds before the next poll/retry.
4892
+
4893
+ Delivered alongside `ValidationErrorEnvelope`,
4894
+ `FeatureNotAvailableResponse`, and
4895
+ `ProcessingClassExceedsBandResponse` via the naked `oneOf` on
4896
+ the 422 response — duck-typed on required-field shape: this
4897
+ branch is the only one carrying `error_type: probe_pending`
4898
+ and it has neither `details` (ValidationErrorEnvelope) nor
4899
+ `violations` (the other two typed envelopes), so it matches
4900
+ exactly one branch.
4312
4901
  allOf:
4313
4902
  - $ref: '#/components/schemas/ErrorEnvelope'
4314
4903
  - type: object
4315
4904
  required:
4316
4905
  - error_type
4317
- - violations
4906
+ - job_ref
4318
4907
  properties:
4319
4908
  error_type:
4320
4909
  type: string
4321
4910
  enum:
4322
- - feature_tier_restricted
4323
- description: Discriminator for the 403 oneOf. Always `feature_tier_restricted`.
4324
- violations:
4325
- type: array
4326
- minItems: 1
4911
+ - probe_pending
4912
+ description: Discriminator for the 422 oneOf. Always `probe_pending`.
4913
+ job_ref:
4914
+ type: string
4915
+ description: |
4916
+ Workflow-local identifier of the job whose upload-probe
4917
+ has not landed — `JobDefinition.id` if the caller
4918
+ supplied one, else the auto-generated `job_N` token.
4919
+ Mirrors `ProcessingClassBandViolation.job_ref`; NOT
4920
+ `format: uuid` because workflow-create rejects fire
4921
+ before server-side UUIDs are assigned.
4922
+ example: "job_0"
4923
+
4924
+ ProcessingClassRejectReason:
4925
+ type: string
4926
+ description: |
4927
+ Why a job cannot be classified — input size/duration exceeds
4928
+ the `long_form` (or `long_form_re_encode`) ceiling under the
4929
+ caller's effective per-tier caps (per
4930
+ [ADR-0011](../docs/decisions/0011-per-tier-processing-class-constraints.md)
4931
+ `per_tier_constraints`). Distinct from `ProcessingClassReason`
4932
+ (which describes WHY a job got its assigned class — success-
4933
+ path advisory).
4934
+
4935
+ Reasons are split by the violated constraint key on the
4936
+ processing-class block (per `schemas/FORMAT.md`
4937
+ §"`processing_class:` block"):
4938
+
4939
+ - `input_size_exceeds_long_form` /
4940
+ `input_duration_exceeds_long_form`: a **single input**
4941
+ exceeds the per-input ceiling (`max_input_size_bytes` /
4942
+ `max_input_duration`). Applies to single-input operations
4943
+ (compress, convert, thumbnail, text_watermark,
4944
+ audio_watermark) AND per-input checks on multi-input
4945
+ operations with per-input caps (image_watermark,
4946
+ custom_luma, audio_overlay).
4947
+ - `combined_size_exceeds_long_form` /
4948
+ `combined_duration_exceeds_long_form`: the **summed** inputs
4949
+ exceed the multi-input combined ceiling
4950
+ (`max_total_input_size_bytes` / `max_total_duration`).
4951
+ Applies to operations whose `processing_class.<class>.
4952
+ constraints` carries `max_total_*` — merge today.
4953
+ enum:
4954
+ - input_size_exceeds_long_form
4955
+ - input_duration_exceeds_long_form
4956
+ - combined_size_exceeds_long_form
4957
+ - combined_duration_exceeds_long_form
4958
+
4959
+ ProcessingClassBandViolation:
4960
+ type: object
4961
+ description: |
4962
+ One band-ceiling overflow on a workflow-create reject. Carries
4963
+ the I26 localisation quad on the violation (mirrors
4964
+ `FeatureViolation`); envelope-level localisation rides via
4965
+ `allOf [ErrorEnvelope]` on `ProcessingClassExceedsBandResponse`.
4966
+
4967
+ `job_ref`, `actual`, `ceiling`, `operation`, and
4968
+ `processing_class` are REQUIRED — the server always knows them
4969
+ at reject time and consumers rely on them to (a) correlate
4970
+ fail-all violations back to the offending job in multi-job
4971
+ workflows and (b) render "X exceeded by Y" without re-deriving
4972
+ caps from the per-tier overlay.
4973
+ required:
4974
+ - reason
4975
+ - job_ref
4976
+ - operation
4977
+ - processing_class
4978
+ - actual
4979
+ - ceiling
4980
+ properties:
4981
+ reason:
4982
+ $ref: '#/components/schemas/ProcessingClassRejectReason'
4983
+ job_ref:
4984
+ type: string
4985
+ description: |
4986
+ Workflow-local job identifier — `JobDefinition.id` if the
4987
+ caller supplied one, otherwise the auto-generated `job_N`
4988
+ positional token assigned by the server during request
4989
+ validation (e.g. `job_0`, `job_1`). Workflow-create rejects
4990
+ fire BEFORE persisted server UUIDs are assigned; `job_ref`
4991
+ is the request-local handle that survives across reject
4992
+ and (later) success paths. Per `JobDefinition.id` pattern
4993
+ + auto-generation rules.
4994
+ example: "stitched"
4995
+ input_index:
4996
+ type: integer
4997
+ minimum: 0
4998
+ description: |
4999
+ 0-based ordinal into `JobDefinition.inputs[]` identifying
5000
+ the specific input that violated the per-input ceiling.
5001
+ Set ONLY on `input_*_exceeds_long_form` reasons for
5002
+ multi-input operations; omitted on single-input
5003
+ operations (no positional ambiguity) and on
5004
+ `combined_*_exceeds_long_form` reasons (whole-job
5005
+ violation across all inputs).
5006
+ operation:
5007
+ $ref: '#/components/schemas/OperationType'
5008
+ description: Operation type that triggered the band-ceiling reject.
5009
+ processing_class:
5010
+ $ref: '#/components/schemas/ProcessingClass'
5011
+ description: |
5012
+ The class whose ceiling was exceeded — typically `long_form`
5013
+ or `long_form_re_encode`.
5014
+ actual:
5015
+ type: integer
5016
+ minimum: 0
5017
+ description: |
5018
+ Observed value. Bytes for `*_size_exceeds_long_form`
5019
+ reasons; whole seconds for `*_duration_exceeds_long_form`
5020
+ reasons.
5021
+ ceiling:
5022
+ type: integer
5023
+ minimum: 0
5024
+ description: |
5025
+ Effective per-tier ceiling for this caller (same units as
5026
+ `actual`). The binding cap after `per_tier_constraints`
5027
+ overlay; lets consumers render "X exceeded by Y" without
5028
+ re-deriving caps from the per-tier overlay.
5029
+ required_tier:
5030
+ description: |
5031
+ Optional. When a HIGHER tier exists whose
5032
+ `per_tier_constraints` would accommodate this request, the
5033
+ server SHOULD set `required_tier` to that tier — consumers
5034
+ can offer an upgrade nudge. `null` (or omitted) means
5035
+ **no tier resolves** the overflow (e.g. enterprise already
5036
+ hits the 120 GB hard ceiling per
5037
+ [ADR-0011](../docs/decisions/0011-per-tier-processing-class-constraints.md)
5038
+ D1–D4).
5039
+
5040
+ Distinct from `TierRestrictionResponse.required_tier`
5041
+ which names a tier whose request-level upload cap
5042
+ satisfies the request — this field names a tier whose
5043
+ long-form processing-class cap satisfies it. Per
5044
+ [ADR-0012](../docs/decisions/0012-processing-class-band-reject-envelope.md)
5045
+ for the band-vs-tier split.
5046
+ oneOf:
5047
+ - $ref: '#/components/schemas/UserTier'
5048
+ - type: 'null'
5049
+ documentation_url:
5050
+ type: string
5051
+ format: uri
5052
+ description: Optional link to processing-class documentation.
5053
+ message_key:
5054
+ type: string
5055
+ description: Per I26. See `ErrorEnvelope.message_key`.
5056
+ message:
5057
+ type: string
5058
+ description: |
5059
+ Per I26. Human-readable, localised per `Accept-Language`.
5060
+ Never parse for control flow.
5061
+ locale:
5062
+ type: string
5063
+ description: BCP 47 locale tag. See `ErrorEnvelope.locale`.
5064
+ message_params:
5065
+ type: object
5066
+ additionalProperties: true
5067
+ description: |
5068
+ Per I26. Optional interpolation values for the localised
5069
+ `message`. Excludes cost numbers.
5070
+
5071
+ ProcessingClassExceedsBandResponse:
5072
+ description: |
5073
+ 422 response body when one or more jobs cannot be classified
5074
+ because input size/duration exceeds the `long_form` (or
5075
+ `long_form_re_encode`) ceiling under the caller's effective
5076
+ per-tier caps (per
5077
+ [ADR-0011](../docs/decisions/0011-per-tier-processing-class-constraints.md)
5078
+ `per_tier_constraints`).
5079
+
5080
+ Distinct from `TierRestrictionResponse` (403, request-level
5081
+ tier-quota — resolvable by upload-size reduction or quota
5082
+ change) and from `FeatureNotAvailableResponse` (422, feature
5083
+ tagged not-yet-shipped — resolvable by waiting for the
5084
+ availability flip). Resolution path is per-violation:
5085
+ `required_tier` on each `ProcessingClassBandViolation` names
5086
+ the tier (if any) whose `per_tier_constraints` would
5087
+ accommodate the request; `null` means terminal (e.g. enterprise
5088
+ already at the 120 GB hard ceiling).
5089
+
5090
+ Per
5091
+ [ADR-0012](../docs/decisions/0012-processing-class-band-reject-envelope.md)
5092
+ for the band-vs-tier rationale and routing rule (why this
5093
+ envelope is 422, not an extension of `TierRestrictionResponse`
5094
+ on 403).
5095
+
5096
+ Delivered on `POST /api/workflows` 422 in a naked `oneOf`
5097
+ alongside `ValidationErrorEnvelope` and
5098
+ `FeatureNotAvailableResponse` — branches distinguished by
5099
+ required-field shape (`details` vs `error_type` value).
5100
+ allOf:
5101
+ - $ref: '#/components/schemas/ErrorEnvelope'
5102
+ - type: object
5103
+ required:
5104
+ - error_type
5105
+ - violations
5106
+ properties:
5107
+ error_type:
5108
+ type: string
5109
+ enum:
5110
+ - processing_class_exceeds_band
5111
+ description: Discriminator for the 422 oneOf. Always `processing_class_exceeds_band` for this envelope.
5112
+ violations:
5113
+ type: array
5114
+ minItems: 1
5115
+ description: |
5116
+ One entry per offending job/input. Per ADR-0001 §F6
5117
+ batched-violations rule, multiple violations in a
5118
+ single request are ALL returned here (fail-all, not
5119
+ fail-fast).
5120
+ items:
5121
+ $ref: '#/components/schemas/ProcessingClassBandViolation'
5122
+
5123
+ FeatureTierRestrictedResponse:
5124
+ description: |
5125
+ 403 response body when a workflow references one or more features
5126
+ gated by a higher subscription tier than the caller has.
5127
+
5128
+ Distinct from `TierRestrictionResponse` (also 403): this envelope
5129
+ is per-feature granularity (operation / mime_group / option /
5130
+ per_value); `TierRestrictionResponse` is request-level (file size
5131
+ or MIME). The two envelopes are discriminated by `error_type` at
5132
+ the response `oneOf` level.
5133
+ allOf:
5134
+ - $ref: '#/components/schemas/ErrorEnvelope'
5135
+ - type: object
5136
+ required:
5137
+ - error_type
5138
+ - violations
5139
+ properties:
5140
+ error_type:
5141
+ type: string
5142
+ enum:
5143
+ - feature_tier_restricted
5144
+ description: Discriminator for the 403 oneOf. Always `feature_tier_restricted`.
5145
+ violations:
5146
+ type: array
5147
+ minItems: 1
4327
5148
  description: |
4328
5149
  One entry per tier-gated feature. Each `FeatureViolation`
4329
5150
  MUST set `required_tier` to the tier that would satisfy
@@ -4509,7 +5330,7 @@ components:
4509
5330
  - thumbnail_office: Office document (DOCX/XLSX/PPTX/ODT/ODS/ODP)
4510
5331
  thumbnail sub-type. Backed by a dedicated LibreOffice Lambda.
4511
5332
  Not yet emitted.
4512
- - image_watermark: Image overlay (file or external_source) onto a base media asset. Multi-input (Path B with role: base + overlay). Stable for image bases (jpeg/png/webp); animated GIF and video bases are advertised as `planned` via parallel mime_groups (`image_gif`, `video`) — dispatch returns `feature_not_available` (422) until Lambda support ships. Per ADR-0004 + I4-CONS + I5 (Trello AKZiOXnd).
5333
+ - image_watermark: Image overlay (file or external_source) onto a base IMAGE asset. Multi-input (Path B with role: base + overlay). Stable for static-image bases (jpeg/png/webp); animated GIF is advertised as `planned` via the `image_gif` mime_group — dispatch returns `feature_not_available` (422) until Lambda support ships. Video bases are NOT supported by image_watermark — use the dedicated `video_watermark` operation per ADR-0013. Per ADR-0004 + I4-CONS + I5 (Trello AKZiOXnd).
4513
5334
  - text_watermark: Text overlay rendered onto an image (Liberation Sans). Single-input. Per ADR-0004 + I4-CONS.
4514
5335
  - merge: Concatenate/combine multiple files into one (images, video, audio). Multi-input. Image inputs merge into animated GIF or slideshow video; image collage/grid and PDF concatenation are not supported by the V1 Lambda.
4515
5336
  - archive: Bundle files into ZIP/tar.gz (all types). Multi-input.
@@ -4517,6 +5338,10 @@ components:
4517
5338
  - custom_luma: Apply a caller-uploaded luma matte to a base video for a custom luma-matte transition effect. Multi-input (`role: base` + `role: transition_mask`). `availability: planned` + `required_tier: pro`; dispatch returns `feature_not_available` (422) until Lambda ships. Distinct from FFmpeg `xfade=custom` (which is an expression, not an operation). Per ticket I29 (Trello EPUE5Vs1).
4518
5339
  - audio_overlay: Mix a secondary audio asset over a primary audio or video base (DJ tags, podcast intros/outros, station IDs, jingles). Multi-input (`role: base` + `role: overlay`). `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. **NOT** the same as `audio_watermark` — that operation is steganographic (imperceptible identifier embedded for ownership tracking), tracked separately by I20. Per ticket I19 (Trello Xr3Z4GBF).
4519
5340
  - audio_watermark: Embed a steganographic forensic watermark into an audio asset (or a video's audio track) — Cinavia / Resemble PerTh territory. Single-input. `availability: planned` + `required_tier: enterprise`; dispatch returns `feature_not_available` (422) until Lambda ships. Pairs with `POST /api/audio-watermark/decode` for own-watermarks-only extraction. Per ticket I20 (Trello omiCq7Vn).
5341
+ - audio_to_video: Produce a video from an audio input plus an OPTIONAL still image overlay. Multi-input role-based with the first OPTIONAL role on the contract (`role: base` audio required, `role: overlay` image 0..1 — see `per_role_cardinality`). When overlay is omitted, the video uses a solid background colour. `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. Per ticket [`SlluxMBN`](https://trello.com/c/SlluxMBN) + ADR-0015 (introduces `per_role_cardinality` vocab).
5342
+ - video_watermark: Apply an image overlay onto a base video via FFmpeg's `overlay` filter. Multi-input role-based (`role: base` video + `role: overlay` image, exactly one of each per `per_role_cardinality`). Re-encode required; audio stream-copy passthrough. Distinct from `image_watermark` (pure-Rust/image-only). `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. Per ticket [`4NrRPCgh`](https://trello.com/c/4NrRPCgh) + ADR-0013.
5343
+ - video_text_watermark: Render a text overlay onto a base video via FFmpeg's `drawtext` filter. Single-input — text and styling in options. Same `watermark_mode` (single/tiled), anchor + margin vocab as `text_watermark`. Re-encode required; audio stream-copy passthrough. `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. Per ticket [`4NrRPCgh`](https://trello.com/c/4NrRPCgh) + ADR-0013.
5344
+ - split: Fan one input file into N outputs across GIF / PDF / audio / video MIME families. Single-input per-mime-group catalog (mirrors merge/convert): GIF uses `frame_range` (REQUIRED) + `output_format`; PDF uses `page_range` OR `page_groups` (mutually exclusive); audio + video use a `mode` discriminator (interval/count/cut_points) + numeric-seconds wire format + `precision` flag (fast/exact). 200-output hard cap per ADR-0009 §D5 with per-mode preflight math; output naming `output-001..output-200`. Long-form video routes to a separate `split-video-fargate` worker via `processing_class`. `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. Per ticket [`vKI0CFDu`](https://trello.com/c/vKI0CFDu) + ADR-0014.
4520
5345
 
4521
5346
  Both the legacy `thumbnail` value and the four sub-type values
4522
5347
  are valid routing targets today during the thumbnail migration
@@ -4541,6 +5366,10 @@ components:
4541
5366
  - custom_luma
4542
5367
  - audio_overlay
4543
5368
  - audio_watermark
5369
+ - audio_to_video
5370
+ - video_watermark
5371
+ - video_text_watermark
5372
+ - split
4544
5373
 
4545
5374
  OperationInputModel:
4546
5375
  type: string
@@ -4548,8 +5377,9 @@ components:
4548
5377
  Whether the operation accepts a single file or multiple files:
4549
5378
  - single: One input file (compress, thumbnail, thumbnail_image,
4550
5379
  thumbnail_video, thumbnail_document, thumbnail_office,
4551
- text_watermark, convert, audio_watermark)
4552
- - multi: Multiple input files (merge, archive, image_watermark, custom_luma, audio_overlay)
5380
+ text_watermark, convert, audio_watermark,
5381
+ video_text_watermark, split)
5382
+ - multi: Multiple input files (merge, archive, image_watermark, custom_luma, audio_overlay, audio_to_video, video_watermark). audio_to_video is the first role-based op with an OPTIONAL role (min_inputs=1, max_inputs=2 — see `per_role_cardinality`); video_watermark mirrors `image_watermark`'s 2-required pattern on video bases.
4553
5383
  enum:
4554
5384
  - single
4555
5385
  - multi
@@ -4634,11 +5464,18 @@ components:
4634
5464
  multipart_chunk_size:
4635
5465
  type: integer
4636
5466
  format: int64
4637
- const: 5242880
5467
+ const: 16777216
4638
5468
  description: |
4639
5469
  Recommended chunk size in bytes for multipart upload
4640
- chunks (5 MiB / 5,242,880 bytes — 1024-based). Matches
4641
- the existing TS SDK default. The server's
5470
+ chunks (16 MiB / 16,777,216 bytes — 1024-based). Raised
5471
+ from 5 MiB by CON-1 (ADR-0011): S3 caps a multipart
5472
+ upload at 10,000 parts, and the Enterprise envelope is a
5473
+ 120 GB hard ceiling. At 16 MiB a 120 GiB object needs
5474
+ 7,680 parts (≤ 10,000, inside AWS's 16–64 MB recommended
5475
+ band); 5 MiB would need 24,576 and 10 MiB 12,288 — both
5476
+ exceed the S3 limit. **Wire-incompatible change** — SDKs
5477
+ pin this as a compile-time constant, so CON-1 and SDK-2
5478
+ ship as a non-independently-mergeable pair. The server's
4642
5479
  `MultipartInitiateResponse` returns a
4643
5480
  `recommended_chunk_size` per file based on first-chunk
4644
5481
  throughput; SDKs SHOULD prefer that runtime value when
@@ -4807,27 +5644,43 @@ components:
4807
5644
  total_parts:
4808
5645
  type: integer
4809
5646
  minimum: 2
4810
- maximum: 500
5647
+ maximum: 10000
4811
5648
  description: |
4812
5649
  Total number of parts. The client slices the remaining file into
4813
5650
  exactly (total_parts - 1) chunks using recommended_chunk_size.
4814
- The last chunk may be smaller.
5651
+ The last chunk may be smaller. Maximum raised 500 → 10,000 by
5652
+ CON-1 (ADR-0011) — 10,000 is the S3 multipart hard part limit;
5653
+ 500 contract-capped uploads at ≈ 50 GB, below the 120 GB
5654
+ Enterprise envelope.
4815
5655
  example: 5
4816
5656
  recommended_chunk_size:
4817
5657
  type: integer
4818
- minimum: 5242880
5658
+ minimum: 16777216
4819
5659
  maximum: 104857600
4820
5660
  description: |
4821
5661
  Chunk size in bytes for remaining parts. Calculated from first chunk
4822
- throughput * 5s target, clamped to 5MB-100MB. The last chunk may be
4823
- smaller than 5MB.
4824
- example: 10485760
5662
+ throughput * 5s target, clamped to 16 MiB–100 MiB. The last chunk
5663
+ may be smaller than 16 MiB. Minimum raised 5 MiB → 16 MiB by CON-1
5664
+ (ADR-0011) so the runtime value never falls below the
5665
+ `UploadThresholds.multipart_chunk_size` constant SDKs fall back to
5666
+ (the S3 10,000-part limit at the 120 GB Enterprise ceiling).
5667
+ example: 16777216
4825
5668
  presigned_urls:
4826
5669
  type: array
4827
5670
  description: |
4828
5671
  Pre-signed S3 PUT URLs for parts 2 through total_parts.
4829
5672
  Each URL accepts a PUT request with raw chunk bytes as body.
4830
5673
  Collect the ETag from each S3 response for the complete request.
5674
+
5675
+ NOTE (CON-1/ADR-0011): with `total_parts` now bounded by the
5676
+ S3 hard limit (10,000), a maximal Enterprise upload implies a
5677
+ large URL set. The server is NOT required to return every URL
5678
+ in one synchronous response — bounded-batch / paginated URL
5679
+ delivery and resumable re-fetch are owned by API-2
5680
+ (durable upload session) and SDK-3 (presigned batching +
5681
+ paginated ListParts). Consumers MUST NOT assume a single
5682
+ initiate response carries all `total_parts - 1` URLs at the
5683
+ high end of the range; treat this array as the first batch.
4831
5684
  items:
4832
5685
  $ref: '#/components/schemas/PresignedUrlPart'
4833
5686
  constraints_applied:
@@ -4940,6 +5793,249 @@ components:
4940
5793
  data:
4941
5794
  $ref: '#/components/schemas/MultipartCompleteResponse'
4942
5795
 
5796
+ # ============================================
5797
+ # MULTIPART SESSION RESUME SCHEMAS (HxUmVr3Y)
5798
+ # ============================================
5799
+
5800
+ MultipartPartListing:
5801
+ type: object
5802
+ description: |
5803
+ One uploaded part as reported by the S3 multipart ListParts
5804
+ API. The /status response carries an array of these. Mirrors
5805
+ the S3 wire shape verbatim so the response is a 1:1 passthrough
5806
+ with no translation layer.
5807
+ required:
5808
+ - part_number
5809
+ - etag
5810
+ - size_bytes
5811
+ - last_modified
5812
+ properties:
5813
+ part_number:
5814
+ type: integer
5815
+ minimum: 1
5816
+ maximum: 10000
5817
+ description: |
5818
+ S3 multipart part number. Part 1 is always present in a
5819
+ healthy session (uploaded synchronously at `/initiate`);
5820
+ parts 2–N follow.
5821
+ etag:
5822
+ type: string
5823
+ description: |
5824
+ ETag returned by S3 for the part. Quoted MD5 hex by default;
5825
+ preserve quotes verbatim as S3 emits them.
5826
+ example: '"d8e8fca2dc0f896fd7cb4cb0031ba249"'
5827
+ size_bytes:
5828
+ type: integer
5829
+ format: int64
5830
+ minimum: 0
5831
+ description: Part size in bytes (as reported by S3).
5832
+ last_modified:
5833
+ type: string
5834
+ format: date-time
5835
+ description: ISO-8601 timestamp from S3 ListParts `LastModified`.
5836
+
5837
+ MultipartStatusResponse:
5838
+ type: object
5839
+ description: |
5840
+ Resume-state snapshot for an in-flight multipart session. The
5841
+ `data` payload on `GET /api/uploads/multipart/{uploadId}/status`.
5842
+ required:
5843
+ - upload_id
5844
+ - multipart_upload_id
5845
+ - cloud_key
5846
+ - total_parts
5847
+ - uploaded_parts
5848
+ - next_part_number_marker
5849
+ - is_truncated
5850
+ - manifest_expires_at
5851
+ - recommended_chunk_size
5852
+ properties:
5853
+ upload_id:
5854
+ $ref: '#/components/schemas/UuidV7'
5855
+ description: Session identifier (matches the path `uploadId` and the initiate response `upload_id`).
5856
+ multipart_upload_id:
5857
+ type: string
5858
+ description: |
5859
+ S3 multipart upload identifier returned by `CreateMultipartUpload`.
5860
+ Opaque to API consumers; emitted for diagnostic visibility
5861
+ (SDK logs / dashboards correlating against AWS CloudTrail).
5862
+ cloud_key:
5863
+ type: string
5864
+ description: |
5865
+ S3 object key under which the assembled object will be
5866
+ stored on `multipart/complete`. Opaque to consumers.
5867
+ total_parts:
5868
+ type: integer
5869
+ minimum: 2
5870
+ maximum: 10000
5871
+ description: |
5872
+ Total number of parts the client must upload to complete
5873
+ the session. Same value as `MultipartInitiateResponse.total_parts`
5874
+ and stable for the lifetime of the session.
5875
+ uploaded_parts:
5876
+ type: array
5877
+ description: |
5878
+ Parts already present in S3 for this session, as reported
5879
+ by S3 ListParts at request time. May be empty (no parts
5880
+ uploaded yet) or partial (resume mid-upload). Order is the
5881
+ S3-native part-number ascending.
5882
+ items:
5883
+ $ref: '#/components/schemas/MultipartPartListing'
5884
+ next_part_number_marker:
5885
+ description: |
5886
+ S3 ListParts cursor — pass this as the next request's
5887
+ `cursor` query parameter to fetch the following page.
5888
+ `null` on the final page (or when `is_truncated: false`).
5889
+ Mirrors the S3 `NextPartNumberMarker` field verbatim.
5890
+
5891
+ Upper bound matches the request `cursor` query parameter
5892
+ range (`0..9999`) so SDKs can round-trip the marker
5893
+ verbatim — `total_parts` caps at 10,000 and the cursor
5894
+ semantically means "list after part N", so the highest
5895
+ reachable marker is 9,999 (any cursor of 10,000 would
5896
+ return an empty page).
5897
+ oneOf:
5898
+ - type: integer
5899
+ minimum: 0
5900
+ maximum: 9999
5901
+ - type: 'null'
5902
+ is_truncated:
5903
+ type: boolean
5904
+ description: |
5905
+ `true` when more pages remain; `false` on the final page.
5906
+ Mirrors the S3 ListParts `IsTruncated` field verbatim.
5907
+ manifest_expires_at:
5908
+ description: |
5909
+ ISO-8601 expiry timestamp of the session manifest. `null`
5910
+ when the TTL has lapsed or was not initialised — in that
5911
+ case, callers SHOULD invoke
5912
+ `POST /api/uploads/multipart/{uploadId}/keepalive` to
5913
+ re-establish a 48 h window before re-presigning. The
5914
+ manifest TTL is decoupled from individual presigned-URL
5915
+ TTLs.
5916
+ oneOf:
5917
+ - type: string
5918
+ format: date-time
5919
+ - type: 'null'
5920
+ recommended_chunk_size:
5921
+ type: integer
5922
+ minimum: 16777216
5923
+ maximum: 104857600
5924
+ description: |
5925
+ Chunk size (bytes) the server recommends for any remaining
5926
+ parts in this session — identical to
5927
+ `MultipartInitiateResponse.recommended_chunk_size` (16 MiB
5928
+ floor / 100 MiB ceiling per CON-1 / ADR-0011). Returned on
5929
+ every page so the client doesn't need to retain the
5930
+ initiate response across a resume.
5931
+
5932
+ MultipartStatusSuccessEnvelope:
5933
+ type: object
5934
+ required:
5935
+ - success
5936
+ - data
5937
+ properties:
5938
+ success:
5939
+ type: boolean
5940
+ enum: [true]
5941
+ data:
5942
+ $ref: '#/components/schemas/MultipartStatusResponse'
5943
+
5944
+ MultipartPresignRequest:
5945
+ type: object
5946
+ description: |
5947
+ Caller-supplied subset of part numbers to re-presign. Request
5948
+ body for `POST /api/uploads/multipart/{uploadId}/presign`.
5949
+ required:
5950
+ - part_numbers
5951
+ properties:
5952
+ part_numbers:
5953
+ type: array
5954
+ minItems: 1
5955
+ maxItems: 100
5956
+ uniqueItems: true
5957
+ description: |
5958
+ Part numbers to re-presign. Each entry MUST satisfy
5959
+ `2 ≤ n ≤ total_parts` from the initiate response —
5960
+ **Part 1 is rejected** because the manifest stores its
5961
+ ETag from `/initiate` and re-presigning would break the
5962
+ eventual `multipart/complete` call. Cap of 100 entries
5963
+ per request; SDKs paginate larger resume sets across
5964
+ multiple `/presign` calls.
5965
+ items:
5966
+ type: integer
5967
+ minimum: 2
5968
+ maximum: 10000
5969
+
5970
+ MultipartPresignResponse:
5971
+ type: object
5972
+ description: |
5973
+ Pre-signed URLs for the parts requested via
5974
+ `MultipartPresignRequest.part_numbers`. The `data` payload on
5975
+ `POST /api/uploads/multipart/{uploadId}/presign`.
5976
+ required:
5977
+ - upload_id
5978
+ - presigned_urls
5979
+ properties:
5980
+ upload_id:
5981
+ $ref: '#/components/schemas/UuidV7'
5982
+ description: Session identifier (echoes the path `uploadId`).
5983
+ presigned_urls:
5984
+ type: array
5985
+ minItems: 1
5986
+ maxItems: 100
5987
+ description: |
5988
+ One pre-signed PUT URL per requested part number, in the
5989
+ same order as `MultipartPresignRequest.part_numbers`. Each
5990
+ item carries `part_number` + `url` + `expires_at` —
5991
+ same shape as `MultipartInitiateResponse.presigned_urls[]`.
5992
+ items:
5993
+ $ref: '#/components/schemas/PresignedUrlPart'
5994
+
5995
+ MultipartPresignSuccessEnvelope:
5996
+ type: object
5997
+ required:
5998
+ - success
5999
+ - data
6000
+ properties:
6001
+ success:
6002
+ type: boolean
6003
+ enum: [true]
6004
+ data:
6005
+ $ref: '#/components/schemas/MultipartPresignResponse'
6006
+
6007
+ MultipartKeepaliveResponse:
6008
+ type: object
6009
+ description: |
6010
+ Refreshed manifest TTL for an in-flight multipart session. The
6011
+ `data` payload on `POST /api/uploads/multipart/{uploadId}/keepalive`.
6012
+ required:
6013
+ - upload_id
6014
+ - manifest_expires_at
6015
+ properties:
6016
+ upload_id:
6017
+ $ref: '#/components/schemas/UuidV7'
6018
+ description: Session identifier (echoes the path `uploadId`).
6019
+ manifest_expires_at:
6020
+ type: string
6021
+ format: date-time
6022
+ description: |
6023
+ ISO-8601 expiry timestamp of the refreshed manifest. Always
6024
+ non-null on a successful 200 response — keepalive
6025
+ unconditionally extends the TTL to 48 h ahead of `now`.
6026
+
6027
+ MultipartKeepaliveSuccessEnvelope:
6028
+ type: object
6029
+ required:
6030
+ - success
6031
+ - data
6032
+ properties:
6033
+ success:
6034
+ type: boolean
6035
+ enum: [true]
6036
+ data:
6037
+ $ref: '#/components/schemas/MultipartKeepaliveResponse'
6038
+
4943
6039
  # ============================================
4944
6040
  # METADATA SCHEMAS
4945
6041
  # ============================================
@@ -5231,10 +6327,21 @@ components:
5231
6327
  type: array
5232
6328
  description: |
5233
6329
  Multi-input list for `merge`, `archive`, `image_watermark`,
5234
- `custom_luma`, and `audio_overlay`. Each entry is a
5235
- `JobInputV2` with its own `WorkflowSource`. Mutually
5236
- exclusive with `source`.
5237
- minItems: 2
6330
+ `custom_luma`, `audio_overlay`, and `audio_to_video`. Each
6331
+ entry is a `JobInputV2` with its own `WorkflowSource`.
6332
+ Mutually exclusive with `source` — the V2 shape boundary
6333
+ stays `source` (single-input) XOR `inputs[]` (multi-input
6334
+ role-based) per ADR-0004 / I12.
6335
+
6336
+ **Minimum input count = sum of role minima** declared in
6337
+ the operation's `per_role_cardinality` (per ticket
6338
+ [`SlluxMBN`](https://trello.com/c/SlluxMBN) ADR-0015).
6339
+ `audio_to_video` requires 1 input (`base` audio; `overlay`
6340
+ optional, 0..1); all other role-based ops require 2+ today.
6341
+ The schema floor here is `minItems: 1` to admit the
6342
+ audio_to_video case; per-operation gates enforce the
6343
+ actual count via `OperationSchemaDefinition.min_inputs`.
6344
+ minItems: 1
5238
6345
  items:
5239
6346
  $ref: '#/components/schemas/JobInputV2'
5240
6347
  operations:
@@ -5246,8 +6353,8 @@ components:
5246
6353
 
5247
6354
  Multi-input jobs (with `inputs[]`) must have exactly one
5248
6355
  operation, and it must be a multi-input type (`merge`,
5249
- `archive`, `image_watermark`, `custom_luma`, or
5250
- `audio_overlay`).
6356
+ `archive`, `image_watermark`, `custom_luma`,
6357
+ `audio_overlay`, or `audio_to_video`).
5251
6358
  items:
5252
6359
  $ref: '#/components/schemas/OperationDefinition'
5253
6360
  deliver:
@@ -5278,7 +6385,7 @@ components:
5278
6385
  items:
5279
6386
  properties:
5280
6387
  type:
5281
- enum: [merge, archive, image_watermark, custom_luma, audio_overlay]
6388
+ enum: [merge, archive, image_watermark, custom_luma, audio_overlay, audio_to_video, video_watermark]
5282
6389
  required: [operations]
5283
6390
  description: |
5284
6391
  Multi-input jobs must have exactly one operation, and it
@@ -5325,6 +6432,21 @@ components:
5325
6432
  `overlay` value already declared for `image_watermark`
5326
6433
  — semantics differ by operation type, but the same
5327
6434
  role label keeps the enum compact.
6435
+ - `audio_to_video`: REQUIRED `base` (audio source); OPTIONAL
6436
+ `overlay` (still image, 0..1). FIRST role-based op with
6437
+ an OPTIONAL role — see `per_role_cardinality` for the
6438
+ machine-readable cardinality declaration (per ticket
6439
+ [`SlluxMBN`](https://trello.com/c/SlluxMBN) /
6440
+ ADR-0015). Reuses `base` + `overlay` role values
6441
+ already declared for `image_watermark` / `audio_overlay`.
6442
+ - `video_watermark`: REQUIRED; values `base` (the source
6443
+ video) or `overlay` (the watermark image). Exactly one
6444
+ of each per job — `per_role_cardinality { base: 1/1,
6445
+ overlay: 1/1 }`. First non-introductory adoption of the
6446
+ `per_role_cardinality` vocab (per ticket
6447
+ [`4NrRPCgh`](https://trello.com/c/4NrRPCgh) /
6448
+ ADR-0013). `video_text_watermark` is single-input (no
6449
+ `role` field — text comes from options).
5328
6450
  - `merge` / `archive`: omit (all inputs share the same role).
5329
6451
 
5330
6452
  `text_watermark` is single-input via `JobDefinition.source`
@@ -5956,7 +7078,9 @@ components:
5956
7078
  UploadProbeResponse:
5957
7079
  type: object
5958
7080
  description: |
5959
- Response body for `POST /api/uploads/{id}/probe` per ticket
7081
+ Payload schema carried on the `data` field of
7082
+ `UploadProbeSuccessEnvelope` returned by
7083
+ `POST /api/uploads/{id}/probe`, per ticket
5960
7084
  [I28 `KbVAnGCm`](https://trello.com/c/KbVAnGCm). Designed
5961
7085
  so SDKs can compile a `client.preflight_clips([file_ids])`
5962
7086
  helper into N parallel probes and aggregate by
@@ -5977,6 +7101,29 @@ components:
5977
7101
  processing_class_pre_assignment:
5978
7102
  $ref: '#/components/schemas/UploadProbeProcessingClass'
5979
7103
 
7104
+ UploadProbeSuccessEnvelope:
7105
+ type: object
7106
+ description: |
7107
+ Success envelope wrapping `UploadProbeResponse` on
7108
+ `POST /api/uploads/{id}/probe` 200 responses, per ticket
7109
+ [`9XlWEnZU`](https://trello.com/c/9XlWEnZU). Mirrors the
7110
+ spec-wide `*SuccessEnvelope` convention (`{success: true,
7111
+ data: <payload>}`) used by every other 2xx endpoint; the
7112
+ probe endpoint shipped during the v2.3 declared-but-pending
7113
+ phase missing the wrap. The API
7114
+ (`ApiResponseTrait::respondSuccess` in `compression_api`)
7115
+ already emits this shape — the envelope brings the contract
7116
+ in line with the wire.
7117
+ required:
7118
+ - success
7119
+ - data
7120
+ properties:
7121
+ success:
7122
+ type: boolean
7123
+ enum: [true]
7124
+ data:
7125
+ $ref: '#/components/schemas/UploadProbeResponse'
7126
+
5980
7127
  # ============================================
5981
7128
  # UPLOAD-SIDE GATING (per ticket I15-CONS F8.1)
5982
7129
  # ============================================
@@ -6293,6 +7440,21 @@ components:
6293
7440
  description: Progress percentage (0-100). Present when in_progress or completed.
6294
7441
  result:
6295
7442
  $ref: '#/components/schemas/OperationResult'
7443
+ error_code:
7444
+ type: string
7445
+ description: |
7446
+ Machine-readable failure code. Present when `status` is `failed`;
7447
+ absent otherwise. Mirrors `SseOperationFailedData.error_code` — the
7448
+ same diagnostic the SSE `operation.failed` event carries, surfaced
7449
+ here for polling consumers. Plain string (consumers duck-type on
7450
+ it), e.g. `output_too_large`.
7451
+ example: "output_too_large"
7452
+ error_message:
7453
+ type: string
7454
+ description: |
7455
+ Human-readable failure detail. Present when `status` is `failed`;
7456
+ absent otherwise. Mirrors `SseOperationFailedData.error_message`.
7457
+ example: "output_too_large: Output (12156489 bytes) is not smaller than input (6187609 bytes)"
6296
7458
 
6297
7459
  OperationResult:
6298
7460
  type: object
@@ -6370,12 +7532,37 @@ components:
6370
7532
 
6371
7533
  OperationDownload:
6372
7534
  type: object
7535
+ description: |
7536
+ A single deliverable output file for an operation. For multi-output
7537
+ fan-out (e.g. convert PDF->image emits one file per page), each entry
7538
+ carries an indexing field. This is the REST projection of the same
7539
+ indexing model the AsyncAPI `OperationResultOutputEntry` defines per
7540
+ ADR-0009 §D2 — `page_index` for PDF-page outputs, `position` for
7541
+ generic ordinals, mutually exclusive within an entry.
6373
7542
  required:
6374
7543
  - operation
6375
7544
  - operation_id
6376
7545
  - filename
6377
7546
  - size_bytes
6378
7547
  - download_url
7548
+ oneOf:
7549
+ - title: PageIndexed
7550
+ description: PDF-page output (convert PDF->image).
7551
+ required: [page_index]
7552
+ not: { required: [position] }
7553
+ - title: PositionIndexed
7554
+ description: Generic ordinal output (frame strip, chapter split).
7555
+ required: [position]
7556
+ not: { required: [page_index] }
7557
+ - title: Unindexed
7558
+ description: |
7559
+ Output without an explicit indexing field — every legacy
7560
+ single-output download. Schema-valid; emitted by all
7561
+ non-fan-out operations.
7562
+ not:
7563
+ anyOf:
7564
+ - required: [page_index]
7565
+ - required: [position]
6379
7566
  properties:
6380
7567
  operation:
6381
7568
  type: string
@@ -6394,6 +7581,26 @@ components:
6394
7581
  type: string
6395
7582
  format: uri
6396
7583
  description: Pre-signed download URL
7584
+ page_index:
7585
+ type: integer
7586
+ minimum: 1
7587
+ description: |
7588
+ 1-based page number for PDF-page fan-out outputs (convert
7589
+ PDF->image). Gapless within an operation (an N-page conversion
7590
+ emits `page_index` 1..N). Mutually exclusive with `position`.
7591
+ Absent on non-indexed (single-output) downloads. Mirrors
7592
+ `OperationResultOutputEntry.page_index`. Per ADR-0009 §D2.
7593
+ example: 1
7594
+ position:
7595
+ type: integer
7596
+ minimum: 0
7597
+ description: |
7598
+ 0-based ordinal for non-PDF multi-output operations (e.g. frame
7599
+ strip, chapter split). Mutually exclusive with `page_index`.
7600
+ Absent on non-indexed downloads. Forward-looking — not emitted by
7601
+ any current operation; declared for parity with
7602
+ `OperationResultOutputEntry.position`. Per ADR-0009 §D2.
7603
+ example: 0
6397
7604
 
6398
7605
  # ============================================
6399
7606
  # SSE EVENT SCHEMAS
@@ -6508,7 +7715,207 @@ components:
6508
7715
  type: integer
6509
7716
  const: 100
6510
7717
  result:
6511
- $ref: '#/components/schemas/OperationResult'
7718
+ $ref: '#/components/schemas/SseOperationCompletionResult'
7719
+
7720
+ SseOperationCompletionResult:
7721
+ description: |
7722
+ Result payload carried on the SSE `operation.completed` event
7723
+ (`SseOperationCompletedData.result`). A 2-branch `oneOf` mirroring the
7724
+ single-output vs multi-output completion split of the AsyncAPI
7725
+ `OperationResult` union (per [ADR-0009](../docs/decisions/0009-multi-output-result-envelope.md)),
7726
+ projected into the client-facing (download-URL) shape rather than the
7727
+ S3-wire shape.
7728
+
7729
+ - **Single-output**: `SseSingleOutputCompletion` (allOf wrap of
7730
+ `OperationResult` + a `result_kind: "single"` discriminator field).
7731
+ `download_url` + `size_bytes`, optional `export_key` / `metrics`. Every
7732
+ operation that produces one canonical output.
7733
+ - **Multi-output**: `SseMultiOutputCompletionWithKind` (allOf wrap of
7734
+ `SseMultiOutputCompletion` + a `result_kind: "multi"` discriminator
7735
+ field). `outputs[]` + `total_output_size_bytes`. Used by convert
7736
+ PDF->image and future fan-out operations.
7737
+
7738
+ Failure is **not** a branch here — it is carried by the separate
7739
+ `operation.failed` event (`SseOperationFailedData`), unlike the AsyncAPI
7740
+ `OperationResult` which folds failure into the same union.
7741
+
7742
+ **Branch dispatch via `result_kind` discriminator.** The branches are
7743
+ disambiguated by an explicit `result_kind` field (`"single"` |
7744
+ `"multi"`) that the API SSE emitter populates. The wrapper allOf shape
7745
+ keeps the underlying `OperationResult` and `SseMultiOutputCompletion`
7746
+ schemas unchanged (they continue to be used elsewhere — REST polling,
7747
+ AsyncAPI Lambda→API — without the discriminator). The discriminator
7748
+ approach replaces the v2.15.1 bare-`$ref` design, which dispatched
7749
+ correctly at validation but generated TS deserialization code that
7750
+ checked camelCase property names against snake_case wire payloads —
7751
+ silent data-loss on every single-output completion. The
7752
+ discriminator-based dispatch reads the snake_case wire key directly
7753
+ (`switch (json['result_kind'])`), avoiding the case-mismatch bug
7754
+ entirely.
7755
+ oneOf:
7756
+ - $ref: '#/components/schemas/SseSingleOutputCompletion'
7757
+ - $ref: '#/components/schemas/SseMultiOutputCompletionWithKind'
7758
+ discriminator:
7759
+ propertyName: result_kind
7760
+ mapping:
7761
+ single: '#/components/schemas/SseSingleOutputCompletion'
7762
+ multi: '#/components/schemas/SseMultiOutputCompletionWithKind'
7763
+
7764
+ SseSingleOutputCompletion:
7765
+ description: |
7766
+ Single-output branch of `SseOperationCompletionResult` — wraps
7767
+ `OperationResult` with a `result_kind: "single"` discriminator field
7768
+ so the openapi-generator dispatch can route by wire-format key
7769
+ (`result_kind`) rather than by structural property presence.
7770
+ See `SseOperationCompletionResult` description for the rationale.
7771
+ allOf:
7772
+ - $ref: '#/components/schemas/OperationResult'
7773
+ - type: object
7774
+ required:
7775
+ - result_kind
7776
+ properties:
7777
+ result_kind:
7778
+ type: string
7779
+ enum: [single]
7780
+ description: |
7781
+ Discriminator. Always the literal `"single"` for this branch.
7782
+ Emitted by the API SSE serializer; consumed by
7783
+ `SseOperationCompletionResult` dispatch.
7784
+
7785
+ SseMultiOutputCompletionWithKind:
7786
+ description: |
7787
+ Multi-output branch of `SseOperationCompletionResult` — wraps
7788
+ `SseMultiOutputCompletion` with a `result_kind: "multi"` discriminator
7789
+ field. See `SseOperationCompletionResult` description for the
7790
+ rationale.
7791
+ allOf:
7792
+ - $ref: '#/components/schemas/SseMultiOutputCompletion'
7793
+ - type: object
7794
+ required:
7795
+ - result_kind
7796
+ properties:
7797
+ result_kind:
7798
+ type: string
7799
+ enum: [multi]
7800
+ description: |
7801
+ Discriminator. Always the literal `"multi"` for this branch.
7802
+ Emitted by the API SSE serializer; consumed by
7803
+ `SseOperationCompletionResult` dispatch.
7804
+
7805
+ SseMultiOutputCompletion:
7806
+ type: object
7807
+ description: |
7808
+ Multi-output completion body for the SSE `operation.completed` event.
7809
+ Per-output details plus an aggregate size, mirroring the AsyncAPI
7810
+ `MultiOutputCompletion` branch of `OperationResult` (per
7811
+ [ADR-0009](../docs/decisions/0009-multi-output-result-envelope.md)) in
7812
+ the client-facing (download-URL) projection.
7813
+ required:
7814
+ - outputs
7815
+ - total_output_size_bytes
7816
+ properties:
7817
+ outputs:
7818
+ type: array
7819
+ minItems: 1
7820
+ maxItems: 200
7821
+ description: |
7822
+ Per-output deliverables for a multi-output operation (e.g. convert
7823
+ PDF->image emits one entry per page). Consumers use `outputs.length`
7824
+ for the count — per ADR-0009 §D4 a denormalised `output_count` was
7825
+ deliberately rejected. `maxItems: 200` mirrors the AsyncAPI
7826
+ `OperationResult.outputs` transport bound.
7827
+ items:
7828
+ $ref: '#/components/schemas/SseMultiOutputResultEntry'
7829
+ total_output_size_bytes:
7830
+ type: integer
7831
+ format: int64
7832
+ minimum: 0
7833
+ description: |
7834
+ Aggregate size of all `outputs[]` in bytes. Equals
7835
+ `sum(outputs[].size_bytes)`. Per ADR-0009 §D4 this is denormalised
7836
+ against the per-entry sum; consumers SHOULD trust the per-entry sum
7837
+ if the two disagree (and log a warning).
7838
+ example: 786432
7839
+ metrics:
7840
+ type: object
7841
+ description: Operation-specific performance metrics (aggregate)
7842
+ properties:
7843
+ compression_ratio:
7844
+ type: number
7845
+ format: double
7846
+ description: Ratio of output size to input size (e.g. 0.45 = 55% reduction)
7847
+ duration_ms:
7848
+ type: integer
7849
+ description: Processing time in milliseconds
7850
+
7851
+ SseMultiOutputResultEntry:
7852
+ type: object
7853
+ description: |
7854
+ A single deliverable output file in an SSE multi-output
7855
+ `operation.completed` result (`SseMultiOutputCompletion.outputs[]`).
7856
+ This is the SSE-local, client-facing twin of the AsyncAPI
7857
+ `OperationResultOutputEntry` (which is S3-wire: `output_key`) and is
7858
+ deliberately **decoupled** from the REST `OperationDownload` schema — it
7859
+ carries `download_url` + `size_bytes` and the same `page_index` /
7860
+ `position` indexing model defined per
7861
+ [ADR-0009](../docs/decisions/0009-multi-output-result-envelope.md) §D2
7862
+ (`page_index` for PDF-page outputs, `position` for generic ordinals,
7863
+ mutually exclusive within an entry).
7864
+ required:
7865
+ - download_url
7866
+ - size_bytes
7867
+ oneOf:
7868
+ - title: PageIndexed
7869
+ description: PDF-page output (convert PDF->image).
7870
+ required: [page_index]
7871
+ not: { required: [position] }
7872
+ - title: PositionIndexed
7873
+ description: Generic ordinal output (frame strip, chapter split).
7874
+ required: [position]
7875
+ not: { required: [page_index] }
7876
+ - title: Unindexed
7877
+ description: |
7878
+ Output without an explicit indexing field. Reserved for future
7879
+ operations that index by something other than page/position.
7880
+ Schema-valid but not currently emitted by any operation.
7881
+ not:
7882
+ anyOf:
7883
+ - required: [page_index]
7884
+ - required: [position]
7885
+ properties:
7886
+ download_url:
7887
+ type: string
7888
+ format: uri
7889
+ description: Pre-signed download URL for this individual output file.
7890
+ size_bytes:
7891
+ type: integer
7892
+ format: int64
7893
+ minimum: 0
7894
+ description: |
7895
+ Size of this individual output file in bytes. `minimum: 0` mirrors
7896
+ the AsyncAPI `OperationResultOutputEntry.output_size_bytes` bound and
7897
+ keeps `total_output_size_bytes` (the sum of these) internally
7898
+ consistent.
7899
+ page_index:
7900
+ type: integer
7901
+ minimum: 1
7902
+ description: |
7903
+ 1-based page number for PDF-page fan-out outputs (convert
7904
+ PDF->image). Gapless within an operation (an N-page conversion
7905
+ emits `page_index` 1..N). Mutually exclusive with `position`.
7906
+ Absent on non-indexed outputs. Mirrors
7907
+ `OperationResultOutputEntry.page_index`. Per ADR-0009 §D2.
7908
+ example: 1
7909
+ position:
7910
+ type: integer
7911
+ minimum: 0
7912
+ description: |
7913
+ 0-based ordinal for non-PDF multi-output operations (e.g. frame
7914
+ strip, chapter split). Mutually exclusive with `page_index`.
7915
+ Absent on non-indexed outputs. Forward-looking — not emitted by any
7916
+ current operation; declared for parity with
7917
+ `OperationResultOutputEntry.position`. Per ADR-0009 §D2.
7918
+ example: 0
6512
7919
 
6513
7920
  SseOperationFailedData:
6514
7921
  type: object
@@ -6755,11 +8162,26 @@ components:
6755
8162
  $ref: '#/components/schemas/OperationInputModel'
6756
8163
  min_inputs:
6757
8164
  type: integer
6758
- description: Minimum number of inputs (multi-input operations only)
6759
- minimum: 2
8165
+ description: |
8166
+ Minimum number of inputs (multi-input operations only).
8167
+ `audio_to_video` declares `min_inputs: 1` (first
8168
+ role-based op with an optional role); all other
8169
+ role-based ops declare `min_inputs: 2`. Per
8170
+ `per_role_cardinality` semantics (ADR-0015), this value
8171
+ equals the sum of role-level minima.
8172
+ minimum: 1
6760
8173
  max_inputs:
6761
8174
  type: integer
6762
8175
  description: Maximum number of inputs (multi-input operations only)
8176
+ per_role_cardinality:
8177
+ $ref: '#/components/schemas/PerRoleCardinality'
8178
+ description: |
8179
+ Optional per-role input-count overlay for role-based
8180
+ multi-input operations. Per ticket
8181
+ [`SlluxMBN`](https://trello.com/c/SlluxMBN) / ADR-0015.
8182
+ Absent on operations whose role rules are still encoded
8183
+ in prose only (image_watermark, audio_overlay,
8184
+ custom_luma — per-op migration follow-ups).
6763
8185
  accepts_mixed_types:
6764
8186
  type: boolean
6765
8187
  description: Whether mixed MIME types are allowed (archive only)