@giveitsmaller/contracts 0.2.3 → 0.3.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 (369) hide show
  1. package/asyncapi/events.yaml +1777 -172
  2. package/availability/availability.json +2403 -0
  3. package/dist/asyncapi/AnonymousSchema_13.d.ts +6 -0
  4. package/dist/asyncapi/AnonymousSchema_13.js +7 -0
  5. package/dist/asyncapi/AnonymousSchema_152.d.ts +5 -0
  6. package/dist/asyncapi/AnonymousSchema_152.js +6 -0
  7. package/dist/asyncapi/Failure.d.ts +25 -0
  8. package/dist/asyncapi/Failure.js +1 -0
  9. package/dist/asyncapi/JobInputRole.d.ts +6 -0
  10. package/dist/asyncapi/JobInputRole.js +7 -0
  11. package/dist/asyncapi/MergeOutputType.d.ts +1 -3
  12. package/dist/asyncapi/MergeOutputType.js +0 -2
  13. package/dist/asyncapi/MultiOutputCompletion.d.ts +25 -0
  14. package/dist/asyncapi/MultiOutputCompletion.js +1 -0
  15. package/dist/asyncapi/NotificationsOperationsQueue.d.ts +4 -2
  16. package/dist/asyncapi/OperationMetrics.d.ts +3 -0
  17. package/dist/asyncapi/OperationProgress.d.ts +2 -0
  18. package/dist/asyncapi/OperationResult.d.ts +4 -19
  19. package/dist/asyncapi/OperationType.d.ts +6 -6
  20. package/dist/asyncapi/OperationType.js +5 -5
  21. package/dist/asyncapi/PageIndexed.d.ts +8 -0
  22. package/dist/asyncapi/PageIndexed.js +1 -0
  23. package/dist/asyncapi/PositionIndexed.d.ts +8 -0
  24. package/dist/asyncapi/PositionIndexed.js +1 -0
  25. package/dist/asyncapi/ProgressStatus.d.ts +3 -0
  26. package/dist/asyncapi/ProgressStatus.js +3 -0
  27. package/dist/asyncapi/ReEncodeDecision.d.ts +5 -0
  28. package/dist/asyncapi/ReEncodeDecision.js +6 -0
  29. package/dist/asyncapi/SingleOutputCompletion.d.ts +25 -0
  30. package/dist/asyncapi/SingleOutputCompletion.js +1 -0
  31. package/dist/asyncapi/SourceEntry.d.ts +2 -1
  32. package/dist/asyncapi/Unindexed.d.ts +8 -0
  33. package/dist/asyncapi/Unindexed.js +1 -0
  34. package/dist/asyncapi/UploadProbeCompletion.d.ts +16 -0
  35. package/dist/asyncapi/UploadProbeCompletion.js +1 -0
  36. package/dist/asyncapi/UploadProbeMediaMetadata.d.ts +18 -0
  37. package/dist/asyncapi/UploadProbeMediaMetadata.js +1 -0
  38. package/dist/asyncapi/UploadProbeProcessingClass.d.ts +6 -0
  39. package/dist/asyncapi/UploadProbeProcessingClass.js +7 -0
  40. package/dist/asyncapi/UploadProbeRequest.d.ts +10 -0
  41. package/dist/asyncapi/UploadProbeRequest.js +1 -0
  42. package/dist/asyncapi/UploadProbeStatus.d.ts +7 -0
  43. package/dist/asyncapi/UploadProbeStatus.js +8 -0
  44. package/dist/asyncapi/index.d.ts +13 -0
  45. package/dist/asyncapi/index.js +4 -0
  46. package/dist/openapi/models/AudioWatermarkDecodeRequest.d.ts +64 -0
  47. package/dist/openapi/models/AudioWatermarkDecodeRequest.js +53 -0
  48. package/dist/openapi/models/AudioWatermarkDecodeResponse.d.ts +80 -0
  49. package/dist/openapi/models/AudioWatermarkDecodeResponse.js +64 -0
  50. package/dist/openapi/models/AuthErrorResponse.d.ts +116 -0
  51. package/dist/openapi/models/AuthErrorResponse.js +66 -0
  52. package/dist/openapi/models/AuthErrorType.d.ts +73 -0
  53. package/dist/openapi/models/AuthErrorType.js +91 -0
  54. package/dist/openapi/models/AvailabilityValue.d.ts +43 -0
  55. package/dist/openapi/models/AvailabilityValue.js +61 -0
  56. package/dist/openapi/models/BalanceExhaustedResponse.d.ts +157 -0
  57. package/dist/openapi/models/BalanceExhaustedResponse.js +86 -0
  58. package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.d.ts +38 -0
  59. package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.js +43 -0
  60. package/dist/openapi/models/CallbackEventType.d.ts +2 -2
  61. package/dist/openapi/models/CallbackEventType.js +2 -2
  62. package/dist/openapi/models/ConnectionSource.d.ts +64 -0
  63. package/dist/openapi/models/ConnectionSource.js +57 -0
  64. package/dist/openapi/models/ContactRequest.d.ts +2 -2
  65. package/dist/openapi/models/ContactRequest.js +2 -2
  66. package/dist/openapi/models/ContactSubject.d.ts +2 -2
  67. package/dist/openapi/models/ContactSubject.js +2 -2
  68. package/dist/openapi/models/ContactValidationErrorResponse.d.ts +2 -2
  69. package/dist/openapi/models/ContactValidationErrorResponse.js +2 -2
  70. package/dist/openapi/models/CreateExternalImport403Response.d.ts +24 -0
  71. package/dist/openapi/models/CreateExternalImport403Response.js +58 -0
  72. package/dist/openapi/models/CreateWorkflow422Response.d.ts +23 -0
  73. package/dist/openapi/models/CreateWorkflow422Response.js +51 -0
  74. package/dist/openapi/models/CreditTransaction.d.ts +186 -0
  75. package/dist/openapi/models/CreditTransaction.js +100 -0
  76. package/dist/openapi/models/CreditTransactionSourceBucket.d.ts +46 -0
  77. package/dist/openapi/models/CreditTransactionSourceBucket.js +64 -0
  78. package/dist/openapi/models/CreditsBalanceResponse.d.ts +84 -0
  79. package/dist/openapi/models/CreditsBalanceResponse.js +68 -0
  80. package/dist/openapi/models/CreditsBalanceSuccessEnvelope.d.ts +46 -0
  81. package/dist/openapi/models/CreditsBalanceSuccessEnvelope.js +54 -0
  82. package/dist/openapi/models/CreditsUsageResponse.d.ts +51 -0
  83. package/dist/openapi/models/CreditsUsageResponse.js +56 -0
  84. package/dist/openapi/models/CreditsUsageSuccessEnvelope.d.ts +46 -0
  85. package/dist/openapi/models/CreditsUsageSuccessEnvelope.js +54 -0
  86. package/dist/openapi/models/Delivery.d.ts +98 -0
  87. package/dist/openapi/models/Delivery.js +65 -0
  88. package/dist/openapi/models/DeliveryOutputRef.d.ts +44 -0
  89. package/dist/openapi/models/DeliveryOutputRef.js +45 -0
  90. package/dist/openapi/models/DeliveryPlan.d.ts +77 -0
  91. package/dist/openapi/models/DeliveryPlan.js +72 -0
  92. package/dist/openapi/models/DeliveryPlanOutput.d.ts +53 -0
  93. package/dist/openapi/models/DeliveryPlanOutput.js +52 -0
  94. package/dist/openapi/models/DeliveryPlanReason.d.ts +35 -0
  95. package/dist/openapi/models/DeliveryPlanReason.js +53 -0
  96. package/dist/openapi/models/DeliverySelection.d.ts +62 -0
  97. package/dist/openapi/models/DeliverySelection.js +54 -0
  98. package/dist/openapi/models/ErrorEnvelope.d.ts +83 -5
  99. package/dist/openapi/models/ErrorEnvelope.js +10 -2
  100. package/dist/openapi/models/EstimateQuality.d.ts +34 -0
  101. package/dist/openapi/models/EstimateQuality.js +52 -0
  102. package/dist/openapi/models/EstimateRange.d.ts +48 -0
  103. package/dist/openapi/models/EstimateRange.js +51 -0
  104. package/dist/openapi/models/ExternalDestination.d.ts +41 -0
  105. package/dist/openapi/models/ExternalDestination.js +47 -0
  106. package/dist/openapi/models/ExternalImportCreatedResponse.d.ts +49 -0
  107. package/dist/openapi/models/ExternalImportCreatedResponse.js +51 -0
  108. package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.d.ts +46 -0
  109. package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.js +54 -0
  110. package/dist/openapi/models/ExternalImportRequest.d.ts +92 -0
  111. package/dist/openapi/models/ExternalImportRequest.js +61 -0
  112. package/dist/openapi/models/ExternalImportToken.d.ts +51 -0
  113. package/dist/openapi/models/ExternalImportToken.js +53 -0
  114. package/dist/openapi/models/ExternalSource.d.ts +31 -0
  115. package/dist/openapi/models/ExternalSource.js +47 -0
  116. package/dist/openapi/models/FeatureNotAvailableResponse.d.ts +140 -0
  117. package/dist/openapi/models/FeatureNotAvailableResponse.js +76 -0
  118. package/dist/openapi/models/FeatureTierRestrictedResponse.d.ts +135 -0
  119. package/dist/openapi/models/FeatureTierRestrictedResponse.js +76 -0
  120. package/dist/openapi/models/FeatureViolation.d.ts +108 -0
  121. package/dist/openapi/models/FeatureViolation.js +63 -0
  122. package/dist/openapi/models/JobDefinition.d.ts +125 -14
  123. package/dist/openapi/models/JobDefinition.js +25 -4
  124. package/dist/openapi/models/JobDownload.d.ts +2 -2
  125. package/dist/openapi/models/JobDownload.js +2 -2
  126. package/dist/openapi/models/JobInputV2.d.ts +89 -0
  127. package/dist/openapi/models/JobInputV2.js +56 -0
  128. package/dist/openapi/models/JobOutputSource.d.ts +57 -0
  129. package/dist/openapi/models/JobOutputSource.js +55 -0
  130. package/dist/openapi/models/JobResponse.d.ts +2 -2
  131. package/dist/openapi/models/JobResponse.js +2 -2
  132. package/dist/openapi/models/JobStatus.d.ts +17 -8
  133. package/dist/openapi/models/JobStatus.js +17 -8
  134. package/dist/openapi/models/JobType.d.ts +2 -2
  135. package/dist/openapi/models/JobType.js +2 -2
  136. package/dist/openapi/models/LivenessResponse.d.ts +2 -2
  137. package/dist/openapi/models/LivenessResponse.js +2 -2
  138. package/dist/openapi/models/LoginUser200Response.d.ts +46 -0
  139. package/dist/openapi/models/LoginUser200Response.js +54 -0
  140. package/dist/openapi/models/LoginUser200ResponseData.d.ts +33 -0
  141. package/dist/openapi/models/LoginUser200ResponseData.js +44 -0
  142. package/dist/openapi/models/LoginUser200ResponseDataUser.d.ts +50 -0
  143. package/dist/openapi/models/LoginUser200ResponseDataUser.js +51 -0
  144. package/dist/openapi/models/LoginUserRequest.d.ts +38 -0
  145. package/dist/openapi/models/LoginUserRequest.js +47 -0
  146. package/dist/openapi/models/LogoutUser200Response.d.ts +50 -0
  147. package/dist/openapi/models/LogoutUser200Response.js +53 -0
  148. package/dist/openapi/models/MetadataResponse.d.ts +13 -2
  149. package/dist/openapi/models/MetadataResponse.js +6 -2
  150. package/dist/openapi/models/MetadataResponseDimensions.d.ts +2 -2
  151. package/dist/openapi/models/MetadataResponseDimensions.js +2 -2
  152. package/dist/openapi/models/MetadataResponseExif.d.ts +2 -2
  153. package/dist/openapi/models/MetadataResponseExif.js +3 -3
  154. package/dist/openapi/models/MetadataResponseExifGps.d.ts +2 -2
  155. package/dist/openapi/models/MetadataResponseExifGps.js +2 -2
  156. package/dist/openapi/models/MetadataSuccessEnvelope.d.ts +3 -3
  157. package/dist/openapi/models/MetadataSuccessEnvelope.js +2 -2
  158. package/dist/openapi/models/MimeGroupSchema.d.ts +46 -2
  159. package/dist/openapi/models/MimeGroupSchema.js +11 -2
  160. package/dist/openapi/models/MultipartCompleteRequest.d.ts +3 -3
  161. package/dist/openapi/models/MultipartCompleteRequest.js +2 -2
  162. package/dist/openapi/models/MultipartCompleteRequestPartsInner.d.ts +2 -2
  163. package/dist/openapi/models/MultipartCompleteRequestPartsInner.js +2 -2
  164. package/dist/openapi/models/MultipartCompleteResponse.d.ts +7 -4
  165. package/dist/openapi/models/MultipartCompleteResponse.js +2 -2
  166. package/dist/openapi/models/MultipartCompleteSuccessEnvelope.d.ts +3 -3
  167. package/dist/openapi/models/MultipartCompleteSuccessEnvelope.js +2 -2
  168. package/dist/openapi/models/MultipartInitiateRequestMetadataHint.d.ts +56 -0
  169. package/dist/openapi/models/MultipartInitiateRequestMetadataHint.js +45 -0
  170. package/dist/openapi/models/MultipartInitiateResponse.d.ts +19 -3
  171. package/dist/openapi/models/MultipartInitiateResponse.js +7 -2
  172. package/dist/openapi/models/MultipartInitiateSuccessEnvelope.d.ts +3 -3
  173. package/dist/openapi/models/MultipartInitiateSuccessEnvelope.js +2 -2
  174. package/dist/openapi/models/OperationDefinition.d.ts +2 -2
  175. package/dist/openapi/models/OperationDefinition.js +2 -2
  176. package/dist/openapi/models/OperationDownload.d.ts +2 -2
  177. package/dist/openapi/models/OperationDownload.js +2 -2
  178. package/dist/openapi/models/OperationInputModel.d.ts +4 -4
  179. package/dist/openapi/models/OperationInputModel.js +4 -4
  180. package/dist/openapi/models/OperationResponse.d.ts +2 -2
  181. package/dist/openapi/models/OperationResponse.js +2 -2
  182. package/dist/openapi/models/OperationResult.d.ts +2 -2
  183. package/dist/openapi/models/OperationResult.js +2 -2
  184. package/dist/openapi/models/OperationResultMetrics.d.ts +2 -2
  185. package/dist/openapi/models/OperationResultMetrics.js +2 -2
  186. package/dist/openapi/models/OperationSchemaDefinition.d.ts +52 -5
  187. package/dist/openapi/models/OperationSchemaDefinition.js +10 -6
  188. package/dist/openapi/models/OperationStatus.d.ts +2 -2
  189. package/dist/openapi/models/OperationStatus.js +2 -2
  190. package/dist/openapi/models/OperationType.d.ts +17 -5
  191. package/dist/openapi/models/OperationType.js +18 -6
  192. package/dist/openapi/models/OperationsSchemaResponse.d.ts +70 -10
  193. package/dist/openapi/models/OperationsSchemaResponse.js +17 -2
  194. package/dist/openapi/models/OptionSchema.d.ts +42 -4
  195. package/dist/openapi/models/OptionSchema.js +12 -2
  196. package/dist/openapi/models/PerValueAvailabilityEntry.d.ts +80 -0
  197. package/dist/openapi/models/PerValueAvailabilityEntry.js +53 -0
  198. package/dist/openapi/models/PresignedUrlPart.d.ts +2 -2
  199. package/dist/openapi/models/PresignedUrlPart.js +3 -3
  200. package/dist/openapi/models/ProcessingClass.d.ts +35 -0
  201. package/dist/openapi/models/ProcessingClass.js +53 -0
  202. package/dist/openapi/models/ProcessingClassHint.d.ts +36 -0
  203. package/dist/openapi/models/ProcessingClassHint.js +54 -0
  204. package/dist/openapi/models/ProcessingClassReason.d.ts +46 -0
  205. package/dist/openapi/models/ProcessingClassReason.js +64 -0
  206. package/dist/openapi/models/ProcessingPlan.d.ts +70 -0
  207. package/dist/openapi/models/ProcessingPlan.js +49 -0
  208. package/dist/openapi/models/ProcessingPlanJob.d.ts +81 -0
  209. package/dist/openapi/models/ProcessingPlanJob.js +71 -0
  210. package/dist/openapi/models/ReadinessResponse.d.ts +2 -2
  211. package/dist/openapi/models/ReadinessResponse.js +2 -2
  212. package/dist/openapi/models/ResponseEnvelope.d.ts +2 -2
  213. package/dist/openapi/models/ResponseEnvelope.js +2 -2
  214. package/dist/openapi/models/RetryResponse.d.ts +5 -5
  215. package/dist/openapi/models/RetryResponse.js +2 -2
  216. package/dist/openapi/models/RetrySuccessEnvelope.d.ts +3 -3
  217. package/dist/openapi/models/RetrySuccessEnvelope.js +2 -2
  218. package/dist/openapi/models/SseEventType.d.ts +2 -2
  219. package/dist/openapi/models/SseEventType.js +2 -2
  220. package/dist/openapi/models/SseJobCompletedData.d.ts +3 -3
  221. package/dist/openapi/models/SseJobCompletedData.js +2 -2
  222. package/dist/openapi/models/SseJobFailedData.d.ts +3 -3
  223. package/dist/openapi/models/SseJobFailedData.js +2 -2
  224. package/dist/openapi/models/SseOperationCompletedData.d.ts +4 -4
  225. package/dist/openapi/models/SseOperationCompletedData.js +2 -2
  226. package/dist/openapi/models/SseOperationFailedData.d.ts +3 -3
  227. package/dist/openapi/models/SseOperationFailedData.js +2 -2
  228. package/dist/openapi/models/SseOperationProgressData.d.ts +61 -3
  229. package/dist/openapi/models/SseOperationProgressData.js +22 -2
  230. package/dist/openapi/models/SseWorkflowTerminalData.d.ts +26 -4
  231. package/dist/openapi/models/SseWorkflowTerminalData.js +4 -2
  232. package/dist/openapi/models/TierRestrictionKind.d.ts +33 -0
  233. package/dist/openapi/models/TierRestrictionKind.js +51 -0
  234. package/dist/openapi/models/TierRestrictionResponse.d.ts +154 -0
  235. package/dist/openapi/models/TierRestrictionResponse.js +83 -0
  236. package/dist/openapi/models/UploadConstraintsApplied.d.ts +70 -0
  237. package/dist/openapi/models/UploadConstraintsApplied.js +57 -0
  238. package/dist/openapi/models/UploadDurationExceedsTierResponse.d.ts +136 -0
  239. package/dist/openapi/models/UploadDurationExceedsTierResponse.js +82 -0
  240. package/dist/openapi/models/UploadFile403Response.d.ts +27 -0
  241. package/dist/openapi/models/UploadFile403Response.js +47 -0
  242. package/dist/openapi/models/UploadFile422Response.d.ts +27 -0
  243. package/dist/openapi/models/UploadFile422Response.js +47 -0
  244. package/dist/openapi/models/UploadProbeMediaMetadata.d.ts +172 -0
  245. package/dist/openapi/models/UploadProbeMediaMetadata.js +69 -0
  246. package/dist/openapi/models/UploadProbeProcessingClass.d.ts +33 -0
  247. package/dist/openapi/models/UploadProbeProcessingClass.js +51 -0
  248. package/dist/openapi/models/UploadProbeResponse.d.ts +58 -0
  249. package/dist/openapi/models/UploadProbeResponse.js +58 -0
  250. package/dist/openapi/models/UploadProbeStatus.d.ts +41 -0
  251. package/dist/openapi/models/UploadProbeStatus.js +59 -0
  252. package/dist/openapi/models/UploadResponse.d.ts +17 -3
  253. package/dist/openapi/models/UploadResponse.js +7 -2
  254. package/dist/openapi/models/UploadSizeExceedsTierResponse.d.ts +136 -0
  255. package/dist/openapi/models/UploadSizeExceedsTierResponse.js +82 -0
  256. package/dist/openapi/models/UploadSource.d.ts +48 -0
  257. package/dist/openapi/models/UploadSource.js +53 -0
  258. package/dist/openapi/models/UploadSuccessEnvelope.d.ts +3 -3
  259. package/dist/openapi/models/UploadSuccessEnvelope.js +2 -2
  260. package/dist/openapi/models/UploadThresholds.d.ts +119 -0
  261. package/dist/openapi/models/UploadThresholds.js +79 -0
  262. package/dist/openapi/models/UserTier.d.ts +41 -0
  263. package/dist/openapi/models/UserTier.js +59 -0
  264. package/dist/openapi/models/ValidationErrorEnvelope.d.ts +51 -4
  265. package/dist/openapi/models/ValidationErrorEnvelope.js +10 -2
  266. package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.d.ts +37 -4
  267. package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.js +6 -2
  268. package/dist/openapi/models/WarningType.d.ts +48 -0
  269. package/dist/openapi/models/WarningType.js +66 -0
  270. package/dist/openapi/models/WebhookOperationContext.d.ts +3 -3
  271. package/dist/openapi/models/WebhookOperationContext.js +2 -2
  272. package/dist/openapi/models/WebhookPayload.d.ts +2 -2
  273. package/dist/openapi/models/WebhookPayload.js +3 -3
  274. package/dist/openapi/models/WorkflowCancelBillingEffect.d.ts +36 -0
  275. package/dist/openapi/models/WorkflowCancelBillingEffect.js +54 -0
  276. package/dist/openapi/models/WorkflowCancelResponse.d.ts +66 -0
  277. package/dist/openapi/models/WorkflowCancelResponse.js +62 -0
  278. package/dist/openapi/models/WorkflowCancelSuccessEnvelope.d.ts +46 -0
  279. package/dist/openapi/models/WorkflowCancelSuccessEnvelope.js +54 -0
  280. package/dist/openapi/models/WorkflowCreateRequest.d.ts +39 -5
  281. package/dist/openapi/models/WorkflowCreateRequest.js +11 -5
  282. package/dist/openapi/models/WorkflowCreateResponse.d.ts +63 -2
  283. package/dist/openapi/models/WorkflowCreateResponse.js +21 -2
  284. package/dist/openapi/models/WorkflowCreateSuccessEnvelope.d.ts +3 -3
  285. package/dist/openapi/models/WorkflowCreateSuccessEnvelope.js +2 -2
  286. package/dist/openapi/models/WorkflowDownloadResponse.d.ts +2 -2
  287. package/dist/openapi/models/WorkflowDownloadResponse.js +2 -2
  288. package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.d.ts +3 -3
  289. package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.js +2 -2
  290. package/dist/openapi/models/WorkflowEdge.d.ts +2 -2
  291. package/dist/openapi/models/WorkflowEdge.js +2 -2
  292. package/dist/openapi/models/WorkflowExpiredResponse.d.ts +126 -0
  293. package/dist/openapi/models/WorkflowExpiredResponse.js +75 -0
  294. package/dist/openapi/models/WorkflowPauseRequiredAction.d.ts +30 -0
  295. package/dist/openapi/models/WorkflowPauseRequiredAction.js +48 -0
  296. package/dist/openapi/models/WorkflowPausedDetail.d.ts +99 -0
  297. package/dist/openapi/models/WorkflowPausedDetail.js +65 -0
  298. package/dist/openapi/models/WorkflowPausedDetailLinks.d.ts +47 -0
  299. package/dist/openapi/models/WorkflowPausedDetailLinks.js +45 -0
  300. package/dist/openapi/models/WorkflowProcessing.d.ts +36 -0
  301. package/dist/openapi/models/WorkflowProcessing.js +42 -0
  302. package/dist/openapi/models/WorkflowResumeResponse.d.ts +55 -0
  303. package/dist/openapi/models/WorkflowResumeResponse.js +57 -0
  304. package/dist/openapi/models/WorkflowResumeSuccessEnvelope.d.ts +46 -0
  305. package/dist/openapi/models/WorkflowResumeSuccessEnvelope.js +54 -0
  306. package/dist/openapi/models/WorkflowSource.d.ts +40 -0
  307. package/dist/openapi/models/WorkflowSource.js +57 -0
  308. package/dist/openapi/models/WorkflowStatus.d.ts +31 -8
  309. package/dist/openapi/models/WorkflowStatus.js +32 -9
  310. package/dist/openapi/models/WorkflowStatusResponse.d.ts +34 -2
  311. package/dist/openapi/models/WorkflowStatusResponse.js +13 -2
  312. package/dist/openapi/models/WorkflowStatusSuccessEnvelope.d.ts +3 -3
  313. package/dist/openapi/models/WorkflowStatusSuccessEnvelope.js +2 -2
  314. package/dist/openapi/models/WorkflowWarning.d.ts +119 -0
  315. package/dist/openapi/models/WorkflowWarning.js +63 -0
  316. package/dist/openapi/models/WorkflowWarningSeverity.d.ts +29 -0
  317. package/dist/openapi/models/WorkflowWarningSeverity.js +47 -0
  318. package/dist/openapi/models/index.d.ts +75 -3
  319. package/dist/openapi/models/index.js +75 -3
  320. package/dist/openapi/runtime.d.ts +2 -2
  321. package/dist/openapi/runtime.js +17 -2
  322. package/dist/operations/archive.metadata.d.ts +2 -0
  323. package/dist/operations/archive.metadata.js +13 -0
  324. package/dist/operations/audio_overlay.d.ts +48 -0
  325. package/dist/operations/audio_overlay.js +19 -0
  326. package/dist/operations/audio_overlay.metadata.d.ts +2 -0
  327. package/dist/operations/audio_overlay.metadata.js +167 -0
  328. package/dist/operations/audio_watermark.d.ts +42 -0
  329. package/dist/operations/audio_watermark.js +37 -0
  330. package/dist/operations/audio_watermark.metadata.d.ts +2 -0
  331. package/dist/operations/audio_watermark.metadata.js +94 -0
  332. package/dist/operations/compress.metadata.d.ts +2 -0
  333. package/dist/operations/compress.metadata.js +205 -0
  334. package/dist/operations/convert.metadata.d.ts +2 -0
  335. package/dist/operations/convert.metadata.js +85 -0
  336. package/dist/operations/custom_luma.d.ts +7 -0
  337. package/dist/operations/custom_luma.js +2 -0
  338. package/dist/operations/custom_luma.metadata.d.ts +2 -0
  339. package/dist/operations/custom_luma.metadata.js +49 -0
  340. package/dist/operations/image_watermark.d.ts +54 -0
  341. package/dist/operations/image_watermark.js +37 -0
  342. package/dist/operations/image_watermark.metadata.d.ts +2 -0
  343. package/dist/operations/image_watermark.metadata.js +97 -0
  344. package/dist/operations/index.d.ts +16 -1
  345. package/dist/operations/index.js +16 -1
  346. package/dist/operations/merge.d.ts +111 -31
  347. package/dist/operations/merge.js +106 -20
  348. package/dist/operations/merge.metadata.d.ts +2 -0
  349. package/dist/operations/merge.metadata.js +385 -0
  350. package/dist/operations/metadata-types.d.ts +47 -0
  351. package/dist/operations/metadata-types.js +2 -0
  352. package/dist/operations/text_watermark.d.ts +31 -0
  353. package/dist/operations/text_watermark.js +22 -0
  354. package/dist/operations/text_watermark.metadata.d.ts +2 -0
  355. package/dist/operations/text_watermark.metadata.js +46 -0
  356. package/dist/operations/thumbnail.metadata.d.ts +2 -0
  357. package/dist/operations/thumbnail.metadata.js +88 -0
  358. package/openapi/api.yaml +5070 -449
  359. package/operations/schemas/audio_overlay.yaml +397 -0
  360. package/operations/schemas/audio_watermark.yaml +184 -0
  361. package/operations/schemas/compress.yaml +20 -3
  362. package/operations/schemas/convert.yaml +18 -3
  363. package/operations/schemas/custom_luma.yaml +109 -0
  364. package/operations/schemas/image_watermark.yaml +280 -0
  365. package/operations/schemas/merge.yaml +295 -66
  366. package/operations/schemas/text_watermark.yaml +147 -0
  367. package/operations/schemas/thumbnail.yaml +15 -0
  368. package/package.json +10 -4
  369. package/operations/schemas/watermark.yaml +0 -87
@@ -1,7 +1,7 @@
1
1
  asyncapi: 3.0.0
2
2
  info:
3
3
  title: GISL Compression Events
4
- version: 3.0.0
4
+ version: 3.2.0
5
5
  description: |
6
6
  Asynchronous event contracts for the GISL (Give It Smaller) compression service.
7
7
 
@@ -16,9 +16,16 @@ info:
16
16
  (`jobs-image`, `jobs-video`, `jobs-audio`, `jobs-document`). Each
17
17
  compression Lambda handles `compress` for exactly one media type.
18
18
  - **`gisl-{env}-{region}-operations`** — non-compression operations
19
- (thumbnail, watermark, merge, archive, convert). Filter attribute:
19
+ (thumbnail, image_watermark, text_watermark, merge, archive, convert,
20
+ custom_luma, audio_overlay, audio_watermark). Filter attribute:
20
21
  `operation_type`. Subscriptions: one queue per operation type, plus four
21
- thumbnail sub-type queues during the migration window.
22
+ thumbnail sub-type queues during the migration window. `custom_luma`,
23
+ `audio_overlay`, and `audio_watermark` are `availability: planned`
24
+ (per [I29](https://trello.com/c/EPUE5Vs1) +
25
+ [I19](https://trello.com/c/Xr3Z4GBF) +
26
+ [I20](https://trello.com/c/omiCq7Vn)) — their queues are declared
27
+ but receive no traffic until Lambda ships. `audio_watermark`
28
+ additionally requires `enterprise` tier.
22
29
 
23
30
  **Publisher branching rule** (implemented in `compression_api`'s
24
31
  `AwsSnsOperationPublisherAdapter` under Option A):
@@ -64,8 +71,13 @@ info:
64
71
  `operation-thumbnail` (legacy, retiring after migration),
65
72
  `operation-thumbnail-image`, `operation-thumbnail-video`,
66
73
  `operation-thumbnail-document`, `operation-thumbnail-office`,
67
- `operation-watermark`, `operation-merge`, `operation-archive`,
68
- `operation-convert`.
74
+ `operation-image-watermark`, `operation-text-watermark`,
75
+ `operation-merge`, `operation-archive`,
76
+ `operation-convert`,
77
+ `operation-custom-luma` (planned per ticket I29 — not yet shipped),
78
+ `operation-audio-overlay` (planned per ticket I19 — not yet shipped),
79
+ `operation-audio-watermark` (planned per ticket I20 —
80
+ enterprise-tier; not yet shipped).
69
81
 
70
82
  **Message Types**
71
83
 
@@ -81,6 +93,22 @@ info:
81
93
  and `OperationRequestAttributes`) so each topic's required attributes can
82
94
  be enforced cleanly without conditional discriminators.
83
95
 
96
+ **Availability metadata.**
97
+
98
+ This spec uses the `x-availability` vendor extension as **decorative
99
+ documentation only**. Per [ADR-0001](../docs/decisions/0001-contract-first-availability.md)
100
+ §1.5, the runtime endpoint `GET /api/operations/schema` (ticket I3) is
101
+ the authoritative source; the sidecar `availability.json` (ticket I3b)
102
+ is the authoritative companion. SDKs MUST NOT depend on
103
+ `x-availability` reaching generated code — consumers read availability
104
+ from the runtime endpoint, not from the generated bindings.
105
+
106
+ The 5-value vocabulary (`stable | beta | experimental | planned |
107
+ deprecated`) is defined in the `AvailabilityValue` schema. See
108
+ `schemas/FORMAT.md` §Availability Taxonomy for operational rules
109
+ (parser obligation: absent = stable; per-enum-value granularity lands
110
+ via ticket I17 `per_value_availability` primitive).
111
+
84
112
  servers:
85
113
  local:
86
114
  host: localhost:4566
@@ -131,7 +159,7 @@ channels:
131
159
  address: gisl-{env}-{region}-operations
132
160
  description: |
133
161
  SNS topic where the API publishes **non-compression** operation
134
- requests (thumbnail, watermark, merge, archive, convert). Filter
162
+ requests (thumbnail, image_watermark, text_watermark, merge, archive, convert, custom_luma, audio_overlay, audio_watermark). Filter
135
163
  attribute: `operation_type` (single-attribute filter policy).
136
164
  Subscriptions fan out to per-operation-type queues, keyed by
137
165
  `operation_type` value.
@@ -142,10 +170,25 @@ channels:
142
170
  - `operation_type = thumbnail_video` -> `ops-thumbnail-video` queue
143
171
  - `operation_type = thumbnail_document` -> `ops-thumbnail-document` queue
144
172
  - `operation_type = thumbnail_office` -> `ops-thumbnail-office` queue
145
- - `operation_type = watermark` -> `ops-watermark` queue
173
+ - `operation_type = image_watermark` -> `ops-image-watermark` queue
174
+ - `operation_type = text_watermark` -> `ops-text-watermark` queue
146
175
  - `operation_type = merge` -> `ops-merge` queue
147
176
  - `operation_type = archive` -> `ops-archive` queue
148
177
  - `operation_type = convert` -> `ops-convert` queue
178
+ - `operation_type = custom_luma` -> `ops-custom-luma` queue (gated
179
+ by `availability: planned` until Lambda ships per
180
+ [I29](https://trello.com/c/EPUE5Vs1) — API rejects workflow-
181
+ create with `feature_not_available` (422) so no traffic reaches
182
+ the topic today)
183
+ - `operation_type = audio_overlay` -> `ops-audio-overlay` queue
184
+ (gated by `availability: planned` until Lambda ships per
185
+ [I19](https://trello.com/c/Xr3Z4GBF) — same `feature_not_
186
+ available` 422 gating)
187
+ - `operation_type = audio_watermark` -> `ops-audio-watermark`
188
+ queue (gated by `availability: planned` +
189
+ `required_tier: enterprise` per
190
+ [I20](https://trello.com/c/omiCq7Vn) — same 422 gating, plus
191
+ a 403 `feature_tier_restricted` for non-enterprise callers)
149
192
 
150
193
  The API's Option A publisher sets `operation_type` as the filter
151
194
  attribute and additionally sets `media_group` as an **informational**
@@ -382,14 +425,41 @@ channels:
382
425
  operationRequest:
383
426
  $ref: '#/components/messages/OperationRequestMessage'
384
427
 
385
- opsWatermark:
386
- address: gisl-{env}-{region}-ops-watermark
428
+ opsImageWatermark:
429
+ address: gisl-{env}-{region}-ops-image-watermark
430
+ description: |
431
+ SQS queue for image_watermark operations (multi-input).
432
+ Subscribed to the `operations` SNS topic with filter
433
+ `operation_type = image_watermark`. Handles image-overlay
434
+ watermarking with one `base` input + one `overlay` input
435
+ (per JobInputV2.role). Backed by the operation-image-watermark
436
+ Lambda (cross-repo X1 retirement target).
437
+
438
+ Stable today for image bases (jpeg/png/webp). Animated GIF and
439
+ video bases are advertised in `image_watermark.yaml` as `planned`
440
+ via parallel mime_groups (`image_gif`, `video` per I5 — Trello
441
+ AKZiOXnd); the publisher MAY route those `media_group` values to
442
+ this queue once Lambda support ships. Until then the API returns
443
+ `feature_not_available` (422) at workflow-create time and these
444
+ messages do not reach the queue. Per ADR-0004 + I4-CONS + I5.
445
+ parameters:
446
+ env:
447
+ description: Environment (local, stg, prod)
448
+ region:
449
+ description: AWS region abbreviation (euw1, use1, etc.)
450
+ messages:
451
+ operationRequest:
452
+ $ref: '#/components/messages/OperationRequestMessage'
453
+
454
+ opsTextWatermark:
455
+ address: gisl-{env}-{region}-ops-text-watermark
387
456
  description: |
388
- SQS queue for watermark operations (image-only).
457
+ SQS queue for text_watermark operations (single-input, image-only).
389
458
  Subscribed to the `operations` SNS topic with filter
390
- `operation_type = watermark`. Handles image-overlay and text-overlay
391
- watermark modes on image inputs. Backed by the watermark Lambda
392
- (`operation-watermark`).
459
+ `operation_type = text_watermark`. Renders text overlays
460
+ (Liberation Sans) supports `single` (one label at anchor) and
461
+ `tiled` (Cinavia-style angled fill) modes. Backed by the
462
+ operation-text-watermark Lambda. Per ADR-0004 + I4-CONS.
393
463
  parameters:
394
464
  env:
395
465
  description: Environment (local, stg, prod)
@@ -405,10 +475,10 @@ channels:
405
475
  SQS queue for merge operations.
406
476
  Subscribed to the `operations` SNS topic with filter
407
477
  `operation_type = merge`. A single `operation-merge` Lambda handles
408
- all merge output types (image collage/grid, animated GIF, video
409
- slideshow/concat, audio concat, PDF concat) — the `output_type`
410
- field on the request determines the encoding path internally, not
411
- the routing.
478
+ all merge output types (animated GIF, video slideshow/concat, audio
479
+ concat) — the `output_type` field on the request determines the
480
+ encoding path internally, not the routing. Image collage and PDF
481
+ concatenation are not supported by the V1 Lambda.
412
482
  parameters:
413
483
  env:
414
484
  description: Environment (local, stg, prod)
@@ -451,6 +521,97 @@ channels:
451
521
  operationRequest:
452
522
  $ref: '#/components/messages/OperationRequestMessage'
453
523
 
524
+ opsCustomLuma:
525
+ address: gisl-{env}-{region}-ops-custom-luma
526
+ description: |
527
+ SQS queue for custom_luma operations (caller-uploaded luma matte
528
+ transition; pro-tier paid feature). Subscribed to the
529
+ `operations` SNS topic with filter `operation_type = custom_luma`.
530
+ Multi-input (Path B with `role: base` + `role: transition_mask`
531
+ per `JobInputV2.role`).
532
+
533
+ `custom_luma` is `availability: planned` + `required_tier: pro`
534
+ per the operation schema; the API rejects workflow-create with
535
+ `feature_not_available` (422) until the Lambda ships, so traffic
536
+ does not reach this queue today. Backed by the
537
+ `operation-custom-luma` Lambda (cross-repo follow-up; not yet
538
+ shipped).
539
+ Per ADR-0004 + I29 (Trello EPUE5Vs1).
540
+ parameters:
541
+ env:
542
+ description: Environment (local, stg, prod)
543
+ region:
544
+ description: AWS region abbreviation (euw1, use1, etc.)
545
+ messages:
546
+ operationRequest:
547
+ $ref: '#/components/messages/OperationRequestMessage'
548
+
549
+ opsAudioOverlay:
550
+ address: gisl-{env}-{region}-ops-audio-overlay
551
+ description: |
552
+ SQS queue for audio_overlay operations (mix a secondary audio
553
+ asset over a primary audio or video base — DJ tags, podcast
554
+ intros/outros, station IDs, jingles). Subscribed to the
555
+ `operations` SNS topic with filter
556
+ `operation_type = audio_overlay`. Multi-input (Path B with
557
+ `role: base` + `role: overlay` per `JobInputV2.role`).
558
+
559
+ Both mime_groups (`audio`, `video`) are `availability: planned`;
560
+ the API rejects workflow-create with `feature_not_available`
561
+ (422) until the Lambda ships, so traffic does not reach this
562
+ queue today. Backed by the `operation-audio-overlay` Lambda
563
+ (cross-repo follow-up; not yet shipped).
564
+
565
+ **NOT** the same as `audio_watermark` — that operation is
566
+ steganographic (imperceptible identifier embedded in audio for
567
+ ownership / forensic tracking) and is owned by ticket I20
568
+ (Trello omiCq7Vn). `audio_overlay` is industry "audio overlay"
569
+ / "audio branding".
570
+
571
+ Per ADR-0004 + I19 (Trello Xr3Z4GBF).
572
+ parameters:
573
+ env:
574
+ description: Environment (local, stg, prod)
575
+ region:
576
+ description: AWS region abbreviation (euw1, use1, etc.)
577
+ messages:
578
+ operationRequest:
579
+ $ref: '#/components/messages/OperationRequestMessage'
580
+
581
+ opsAudioWatermark:
582
+ address: gisl-{env}-{region}-ops-audio-watermark
583
+ description: |
584
+ SQS queue for audio_watermark operations (steganographic
585
+ forensic watermarking — Cinavia / Resemble PerTh territory).
586
+ Subscribed to the `operations` SNS topic with filter
587
+ `operation_type = audio_watermark`. Single-input.
588
+
589
+ Both mime_groups (`audio`, `video` — embed in video's audio
590
+ track) are `availability: planned` + `required_tier:
591
+ enterprise` per the operation schema; the API rejects workflow-
592
+ create with `feature_not_available` (422) until the Lambda
593
+ ships, plus 403 `feature_tier_restricted` for non-enterprise
594
+ callers, so traffic does not reach this queue today. Backed
595
+ by the `operation-audio-watermark` Lambda (cross-repo follow-up;
596
+ not yet shipped).
597
+
598
+ **Industry naming.** This is *audio watermarking* (steganographic
599
+ ownership / forensic tracking), NOT *audio overlay* (audible
600
+ mixing). Audio overlay is owned by `audio_overlay` (I19).
601
+
602
+ Pairs with `POST /api/audio-watermark/decode` for
603
+ own-watermarks-only extraction (declared in `openapi/api.yaml`).
604
+
605
+ Per ADR-0004 + I20 (Trello omiCq7Vn).
606
+ parameters:
607
+ env:
608
+ description: Environment (local, stg, prod)
609
+ region:
610
+ description: AWS region abbreviation (euw1, use1, etc.)
611
+ messages:
612
+ operationRequest:
613
+ $ref: '#/components/messages/OperationRequestMessage'
614
+
454
615
  # ============================================
455
616
  # OPS FAMILY - DEAD LETTER QUEUES
456
617
  # ============================================
@@ -500,9 +661,18 @@ channels:
500
661
  region:
501
662
  description: AWS region abbreviation (euw1, use1, etc.)
502
663
 
503
- opsWatermarkDlq:
504
- address: gisl-{env}-{region}-ops-watermark-dlq
505
- description: DLQ for failed watermark operations. Messages land here after 5 failed processing attempts.
664
+ opsImageWatermarkDlq:
665
+ address: gisl-{env}-{region}-ops-image-watermark-dlq
666
+ description: DLQ for failed image_watermark operations. Messages land here after 5 failed processing attempts.
667
+ parameters:
668
+ env:
669
+ description: Environment (local, stg, prod)
670
+ region:
671
+ description: AWS region abbreviation (euw1, use1, etc.)
672
+
673
+ opsTextWatermarkDlq:
674
+ address: gisl-{env}-{region}-ops-text-watermark-dlq
675
+ description: DLQ for failed text_watermark operations. Messages land here after 5 failed processing attempts.
506
676
  parameters:
507
677
  env:
508
678
  description: Environment (local, stg, prod)
@@ -536,6 +706,152 @@ channels:
536
706
  region:
537
707
  description: AWS region abbreviation (euw1, use1, etc.)
538
708
 
709
+ opsCustomLumaDlq:
710
+ address: gisl-{env}-{region}-ops-custom-luma-dlq
711
+ description: |
712
+ DLQ for failed custom_luma operations (per ticket I29).
713
+ Messages land here after 5 failed processing attempts. No traffic
714
+ reaches this queue while custom_luma is `availability: planned`.
715
+ parameters:
716
+ env:
717
+ description: Environment (local, stg, prod)
718
+ region:
719
+ description: AWS region abbreviation (euw1, use1, etc.)
720
+
721
+ opsAudioOverlayDlq:
722
+ address: gisl-{env}-{region}-ops-audio-overlay-dlq
723
+ description: |
724
+ DLQ for failed audio_overlay operations (per ticket I19).
725
+ Messages land here after 5 failed processing attempts. No
726
+ traffic reaches this queue while audio_overlay is
727
+ `availability: planned`.
728
+ parameters:
729
+ env:
730
+ description: Environment (local, stg, prod)
731
+ region:
732
+ description: AWS region abbreviation (euw1, use1, etc.)
733
+
734
+ opsAudioWatermarkDlq:
735
+ address: gisl-{env}-{region}-ops-audio-watermark-dlq
736
+ description: |
737
+ DLQ for failed audio_watermark operations (per ticket I20).
738
+ Messages land here after 5 failed processing attempts. No
739
+ traffic reaches this queue while audio_watermark is
740
+ `availability: planned` + `required_tier: enterprise`.
741
+ parameters:
742
+ env:
743
+ description: Environment (local, stg, prod)
744
+ region:
745
+ description: AWS region abbreviation (euw1, use1, etc.)
746
+
747
+ # ============================================
748
+ # UPLOAD PROBE (per ticket vBlEurU7 + M2 Epic SrHwuvIl)
749
+ # ============================================
750
+
751
+ uploadProbeRequestsTopic:
752
+ address: gisl-{env}-{region}-upload-probe-requests
753
+ description: |
754
+ SNS topic where the API publishes asynchronous upload-probe
755
+ requests for eager `rich_metadata` enrichment on upload finalize.
756
+ Per ticket [vBlEurU7](https://trello.com/c/vBlEurU7) +
757
+ M2 Epic re-scope ([SrHwuvIl](https://trello.com/c/SrHwuvIl)).
758
+
759
+ The probe Lambda ([rdBHmTfa](https://trello.com/c/rdBHmTfa))
760
+ can be invoked two ways with the same `UploadProbeRequest`
761
+ payload shape:
762
+
763
+ 1. **Synchronous AWS Lambda Invoke** (RequestResponse mode) —
764
+ used to back `POST /api/uploads/{id}/probe`. Not modelled
765
+ as an AsyncAPI channel; the request payload + the response
766
+ payload (`UploadProbeResponse` from `openapi/api.yaml`) are
767
+ the contract for that path.
768
+ 2. **Asynchronous SNS -> SQS -> Lambda** — used by the upload
769
+ finalize publisher. This topic is the entry point for that
770
+ async branch. The Lambda then publishes the result to the
771
+ `uploadProbeCompletionsQueue` for the API consumer to read.
772
+
773
+ No SNS message attribute filter is applied — the topic has a
774
+ single subscription (`uploadProbeQueue`) and the Lambda accepts
775
+ every probe request published here.
776
+ parameters:
777
+ env:
778
+ description: Environment (local, stg, prod)
779
+ region:
780
+ description: AWS region abbreviation (euw1, use1, etc.)
781
+ messages:
782
+ uploadProbeRequest:
783
+ $ref: '#/components/messages/UploadProbeRequestMessage'
784
+
785
+ uploadProbeQueue:
786
+ address: gisl-{env}-{region}-upload-probe
787
+ description: |
788
+ SQS queue subscribed to the `upload-probe-requests` SNS topic.
789
+ The upload-probe Lambda consumes from this queue.
790
+
791
+ Standard (non-FIFO) — each probe request is independent; no
792
+ ordering guarantees are required across uploads.
793
+ parameters:
794
+ env:
795
+ description: Environment (local, stg, prod)
796
+ region:
797
+ description: AWS region abbreviation (euw1, use1, etc.)
798
+ messages:
799
+ uploadProbeRequest:
800
+ $ref: '#/components/messages/UploadProbeRequestMessage'
801
+
802
+ uploadProbeDlq:
803
+ address: gisl-{env}-{region}-upload-probe-dlq
804
+ description: |
805
+ DLQ for failed upload-probe Lambda invocations from the async
806
+ branch. Messages land here after 5 failed processing attempts.
807
+ The sync `POST /api/uploads/{id}/probe` path does not flow
808
+ through this DLQ — sync errors are surfaced inline via the
809
+ Lambda RequestResponse return value.
810
+ parameters:
811
+ env:
812
+ description: Environment (local, stg, prod)
813
+ region:
814
+ description: AWS region abbreviation (euw1, use1, etc.)
815
+
816
+ uploadProbeCompletionsQueue:
817
+ address: gisl-{env}-{region}-upload-probe-completions
818
+ description: |
819
+ SQS queue where the upload-probe Lambda publishes
820
+ `UploadProbeCompletion` messages on the async branch. The API
821
+ consumer worker reads from this queue and persists
822
+ `rich_metadata` on the upload row.
823
+
824
+ Standard (non-FIFO) — completions are independent; no ordering
825
+ guarantees across uploads. Idempotency is handled by the
826
+ `idempotency_key` field on the message payload (echoed from the
827
+ original request) and by the API consumer's per-upload
828
+ "first-write-wins" persistence path.
829
+
830
+ The sync `POST /api/uploads/{id}/probe` path does not publish
831
+ here — sync results are returned inline via the Lambda
832
+ RequestResponse return value.
833
+ parameters:
834
+ env:
835
+ description: Environment (local, stg, prod)
836
+ region:
837
+ description: AWS region abbreviation (euw1, use1, etc.)
838
+ messages:
839
+ uploadProbeCompletion:
840
+ $ref: '#/components/messages/UploadProbeCompletionMessage'
841
+
842
+ uploadProbeCompletionsDlq:
843
+ address: gisl-{env}-{region}-upload-probe-completions-dlq
844
+ description: |
845
+ DLQ for failed completion-message processing by the API
846
+ consumer worker. Messages land here after 5 failed processing
847
+ attempts (e.g. database persistence failures, malformed
848
+ payloads).
849
+ parameters:
850
+ env:
851
+ description: Environment (local, stg, prod)
852
+ region:
853
+ description: AWS region abbreviation (euw1, use1, etc.)
854
+
539
855
  # ============================================
540
856
  # NOTIFICATIONS (Lambda -> API)
541
857
  # ============================================
@@ -627,8 +943,9 @@ operations:
627
943
  Non-compression-branch publisher path. Invoked by the API for any
628
944
  operation where `operation_type != compress` — that is, thumbnail
629
945
  (including the four sub-type values during the migration window),
630
- watermark, merge, archive, convert. The target SNS topic filters
631
- subscriptions on `operation_type` alone.
946
+ image_watermark, text_watermark, merge, archive, convert,
947
+ custom_luma, audio_overlay, audio_watermark. The target SNS
948
+ topic filters subscriptions on `operation_type` alone.
632
949
 
633
950
  **Message attributes set on this branch:** `{operation_type,
634
951
  media_group}`, with `media_group` omitted for `archive` (which is
@@ -653,8 +970,9 @@ operations:
653
970
  description: |
654
971
  The image compression Lambda consumes jobs for image inputs.
655
972
  Handles `compress` only. Non-compression image operations
656
- (thumbnail, watermark, convert, merge) are routed to their
657
- respective queues on the ops-family under the `operations` topic.
973
+ (thumbnail, image_watermark, text_watermark, convert, merge) are
974
+ routed to their respective queues on the ops-family under the
975
+ `operations` topic.
658
976
  messages:
659
977
  - $ref: '#/channels/jobsImage/messages/jobRequest'
660
978
 
@@ -764,17 +1082,36 @@ operations:
764
1082
  messages:
765
1083
  - $ref: '#/channels/opsThumbnailOffice/messages/operationRequest'
766
1084
 
767
- consumeWatermarkOperation:
1085
+ consumeImageWatermarkOperation:
768
1086
  action: receive
769
1087
  channel:
770
- $ref: '#/channels/opsWatermark'
771
- summary: Process watermark operation
1088
+ $ref: '#/channels/opsImageWatermark'
1089
+ summary: Process image_watermark operation
772
1090
  description: |
773
- The watermark Lambda consumes watermark requests routed by
774
- `operation_type=watermark`. Image-only — handles both
775
- image-overlay and text-overlay modes.
1091
+ The operation-image-watermark Lambda consumes image_watermark
1092
+ requests routed by `operation_type=image_watermark`. Multi-input
1093
+ (Path B with role: base + overlay per JobInputV2.role).
1094
+
1095
+ Stable for image bases today; animated GIF (`image_gif` group) and
1096
+ video bases (`video` group) are advertised as `planned` in the
1097
+ schema (I5 — Trello AKZiOXnd) and dispatch returns
1098
+ `feature_not_available` (422) at the API edge until Lambda support
1099
+ ships. Per ADR-0004 + I4-CONS + I5.
776
1100
  messages:
777
- - $ref: '#/channels/opsWatermark/messages/operationRequest'
1101
+ - $ref: '#/channels/opsImageWatermark/messages/operationRequest'
1102
+
1103
+ consumeTextWatermarkOperation:
1104
+ action: receive
1105
+ channel:
1106
+ $ref: '#/channels/opsTextWatermark'
1107
+ summary: Process text_watermark operation
1108
+ description: |
1109
+ The operation-text-watermark Lambda consumes text_watermark
1110
+ requests routed by `operation_type=text_watermark`. Single-input;
1111
+ image-only. Renders text via bundled Liberation Sans (SIL OFL),
1112
+ supports `single` and `tiled` watermark_mode. Per ADR-0004 + I4-CONS.
1113
+ messages:
1114
+ - $ref: '#/channels/opsTextWatermark/messages/operationRequest'
778
1115
 
779
1116
  consumeMergeOperation:
780
1117
  action: receive
@@ -783,10 +1120,11 @@ operations:
783
1120
  summary: Process merge operation
784
1121
  description: |
785
1122
  The merge Lambda consumes merge requests routed by
786
- `operation_type=merge`. A single Lambda handles all merge output
787
- types (image collage/grid, animated GIF, video slideshow/concat,
788
- audio concat, PDF concat). The `output_type` field on the request
789
- determines the encoding path internally.
1123
+ `operation_type=merge`. A single Lambda handles the supported merge
1124
+ output types (animated GIF, video slideshow/concat, audio concat).
1125
+ The `output_type` field on the request determines the encoding
1126
+ path internally. Image collage and PDF concatenation are not
1127
+ supported by the V1 Lambda.
790
1128
  messages:
791
1129
  - $ref: '#/channels/opsMerge/messages/operationRequest'
792
1130
 
@@ -814,6 +1152,59 @@ operations:
814
1152
  messages:
815
1153
  - $ref: '#/channels/opsConvert/messages/operationRequest'
816
1154
 
1155
+ consumeCustomLumaOperation:
1156
+ action: receive
1157
+ channel:
1158
+ $ref: '#/channels/opsCustomLuma'
1159
+ summary: Process custom_luma operation
1160
+ description: |
1161
+ The `operation-custom-luma` Lambda (cross-repo follow-up — not
1162
+ yet shipped) consumes custom_luma requests routed by
1163
+ `operation_type=custom_luma`. Multi-input (Path B with
1164
+ `role: base` + `role: transition_mask` per `JobInputV2.role`).
1165
+ `availability: planned` + `required_tier: pro`; the API rejects
1166
+ workflow-create with `feature_not_available` (422) until the
1167
+ Lambda ships, so traffic does not reach this channel today.
1168
+ Per ADR-0004 + I29 (Trello EPUE5Vs1).
1169
+ messages:
1170
+ - $ref: '#/channels/opsCustomLuma/messages/operationRequest'
1171
+
1172
+ consumeAudioOverlayOperation:
1173
+ action: receive
1174
+ channel:
1175
+ $ref: '#/channels/opsAudioOverlay'
1176
+ summary: Process audio_overlay operation
1177
+ description: |
1178
+ The `operation-audio-overlay` Lambda (cross-repo follow-up —
1179
+ not yet shipped) consumes audio_overlay requests routed by
1180
+ `operation_type=audio_overlay`. Multi-input (Path B with
1181
+ `role: base` + `role: overlay` per `JobInputV2.role`).
1182
+ Both mime_groups (`audio`, `video`) are `availability: planned`;
1183
+ the API rejects workflow-create with `feature_not_available`
1184
+ (422) until the Lambda ships, so traffic does not reach this
1185
+ channel today.
1186
+ Per ADR-0004 + I19 (Trello Xr3Z4GBF).
1187
+ messages:
1188
+ - $ref: '#/channels/opsAudioOverlay/messages/operationRequest'
1189
+
1190
+ consumeAudioWatermarkOperation:
1191
+ action: receive
1192
+ channel:
1193
+ $ref: '#/channels/opsAudioWatermark'
1194
+ summary: Process audio_watermark operation
1195
+ description: |
1196
+ The `operation-audio-watermark` Lambda (cross-repo follow-up —
1197
+ not yet shipped) consumes audio_watermark requests routed by
1198
+ `operation_type=audio_watermark`. Single-input. Both mime_groups
1199
+ (`audio`, `video`) are `availability: planned` +
1200
+ `required_tier: enterprise`; the API rejects workflow-create
1201
+ with `feature_not_available` (422) until the Lambda ships, plus
1202
+ 403 `feature_tier_restricted` for non-enterprise callers, so
1203
+ traffic does not reach this channel today.
1204
+ Per ADR-0004 + I20 (Trello omiCq7Vn).
1205
+ messages:
1206
+ - $ref: '#/channels/opsAudioWatermark/messages/operationRequest'
1207
+
817
1208
  # ============================================
818
1209
  # NOTIFICATION OPERATIONS
819
1210
  # ============================================
@@ -853,6 +1244,81 @@ operations:
853
1244
  - $ref: '#/channels/notificationsOperationsQueue/messages/operationProgress'
854
1245
  - $ref: '#/channels/notificationsOperationsQueue/messages/operationResult'
855
1246
 
1247
+ # ============================================
1248
+ # UPLOAD-PROBE OPERATIONS (per ticket vBlEurU7)
1249
+ # ============================================
1250
+
1251
+ publishUploadProbeRequest:
1252
+ action: send
1253
+ channel:
1254
+ $ref: '#/channels/uploadProbeRequestsTopic'
1255
+ summary: Publish an async upload-probe request
1256
+ description: |
1257
+ The API publishes to this topic on the **async branch** —
1258
+ typically from the upload-finalize publisher when eager
1259
+ `rich_metadata` enrichment is configured for the caller's
1260
+ tier / MIME group.
1261
+
1262
+ The `POST /api/uploads/{id}/probe` sync path does **not** use
1263
+ this operation — it invokes the Lambda directly via the AWS
1264
+ Lambda Invoke API (RequestResponse mode). The two paths share
1265
+ the `UploadProbeRequest` payload schema so the Lambda accepts
1266
+ a single request shape regardless of invocation source.
1267
+ messages:
1268
+ - $ref: '#/channels/uploadProbeRequestsTopic/messages/uploadProbeRequest'
1269
+
1270
+ consumeUploadProbeRequest:
1271
+ action: receive
1272
+ channel:
1273
+ $ref: '#/channels/uploadProbeQueue'
1274
+ summary: Consume an upload-probe request (async branch)
1275
+ description: |
1276
+ The upload-probe Lambda consumes from the SQS queue subscribed
1277
+ to the `upload-probe-requests` topic. On success the Lambda
1278
+ publishes an `UploadProbeCompletion` to the completions queue;
1279
+ on failure the Lambda emits a completion with `error_code`
1280
+ populated (so the API consumer can persist the failure mode)
1281
+ before SQS retry / DLQ handling kicks in.
1282
+
1283
+ Sync invocations bypass this operation entirely.
1284
+ messages:
1285
+ - $ref: '#/channels/uploadProbeQueue/messages/uploadProbeRequest'
1286
+
1287
+ publishUploadProbeCompletion:
1288
+ action: send
1289
+ channel:
1290
+ $ref: '#/channels/uploadProbeCompletionsQueue'
1291
+ summary: Publish an upload-probe completion (async branch)
1292
+ description: |
1293
+ The upload-probe Lambda publishes the result of the async
1294
+ branch to this queue. The payload shape is `UploadProbeCompletion`
1295
+ and intentionally carries both the success fields
1296
+ (`probe_status`, `media_metadata`, `processing_class_pre_assignment`)
1297
+ and the failure fields (`error_code`, `error_message`) so the
1298
+ API consumer worker handles both outcomes from a single
1299
+ message shape.
1300
+
1301
+ The sync `POST /api/uploads/{id}/probe` path does not publish
1302
+ here — sync results are returned inline via the Lambda
1303
+ RequestResponse return value as `UploadProbeResponse`
1304
+ (`openapi/api.yaml`).
1305
+ messages:
1306
+ - $ref: '#/channels/uploadProbeCompletionsQueue/messages/uploadProbeCompletion'
1307
+
1308
+ consumeUploadProbeCompletion:
1309
+ action: receive
1310
+ channel:
1311
+ $ref: '#/channels/uploadProbeCompletionsQueue'
1312
+ summary: Consume an upload-probe completion (async branch)
1313
+ description: |
1314
+ The API consumer worker reads completions from this queue and
1315
+ persists `rich_metadata` (and any error state) on the upload
1316
+ row. Idempotency is handled by the `idempotency_key` echoed in
1317
+ the completion payload plus a per-upload "first-write-wins"
1318
+ persistence path on the API side.
1319
+ messages:
1320
+ - $ref: '#/channels/uploadProbeCompletionsQueue/messages/uploadProbeCompletion'
1321
+
856
1322
  components:
857
1323
  messages:
858
1324
  # ============================================
@@ -933,16 +1399,19 @@ components:
933
1399
  summary: Request to process a non-compression operation
934
1400
  description: |
935
1401
  Message sent by the API to request processing of any
936
- **non-compression** operation (thumbnail, watermark, merge,
937
- archive, convert). Published to the `operations` SNS topic;
938
- routed by the single-attribute filter `operation_type` to one
939
- of the nine ops-family queues.
1402
+ **non-compression** operation (thumbnail, image_watermark,
1403
+ text_watermark, merge, archive, convert, custom_luma,
1404
+ audio_overlay, audio_watermark). Published to the
1405
+ `operations` SNS topic; routed by the single-attribute filter
1406
+ `operation_type` to one of the ops-family queues.
940
1407
 
941
1408
  **SNS Message Attributes:**
942
1409
  - `operation_type`: always present. Values: `thumbnail`,
943
1410
  `thumbnail_image`, `thumbnail_video`, `thumbnail_document`,
944
- `thumbnail_office`, `watermark`, `merge`, `archive`, `convert`.
945
- Used by SNS as the filter attribute on the `operations` topic.
1411
+ `thumbnail_office`, `image_watermark`, `text_watermark`,
1412
+ `merge`, `archive`, `convert`, `custom_luma`,
1413
+ `audio_overlay`, `audio_watermark`. Used by SNS as the
1414
+ filter attribute on the `operations` topic.
946
1415
  - `media_group`: informational metadata, present for every
947
1416
  operation except `archive` (which is media-agnostic). Values:
948
1417
  `image`, `video`, `audio`, `document`. **Not** used by SNS as
@@ -950,12 +1419,23 @@ components:
950
1419
  log correlation.
951
1420
 
952
1421
  **Input models:**
953
- - Single-input operations (thumbnail, thumbnail_image,
954
- thumbnail_video, thumbnail_document, thumbnail_office,
955
- watermark, convert): use `source_bucket` + `source_key`.
956
- `file_type` required.
957
- - Multi-input operations (merge, archive): use `sources` array.
958
- Merge additionally requires `output_type`.
1422
+ - Single-input operations (thumbnail, text_watermark, convert,
1423
+ audio_watermark): use `source_bucket` + `source_key`.
1424
+ `file_type` required. For `thumbnail`, the SNS
1425
+ `operation_type` attribute is permitted to carry a per-media
1426
+ sub-type value for routing (see `OperationRequestAttributes`
1427
+ and the migration-window note below); the payload
1428
+ `operation_type` field always remains `thumbnail`.
1429
+ `audio_watermark` is `availability: planned` +
1430
+ `required_tier: enterprise` so workflow-create rejects with
1431
+ 422 + 403 today.
1432
+ - Multi-input operations (merge, archive, image_watermark,
1433
+ custom_luma, audio_overlay): use `sources` array. Merge
1434
+ additionally requires `output_type`. image_watermark,
1435
+ custom_luma, and audio_overlay use `role` per
1436
+ `JobInputV2.role`. custom_luma and audio_overlay are
1437
+ `availability: planned` so workflow-create rejects with 422
1438
+ today.
959
1439
 
960
1440
  **Migration window note.** During the thumbnail sub-type
961
1441
  migration, both `operation_type=thumbnail` (legacy) and
@@ -988,14 +1468,14 @@ components:
988
1468
  fit: "crop"
989
1469
  format: "jpg"
990
1470
  - name: Thumbnail Image (sub-type)
991
- summary: Generate a thumbnail for an image using the new thumbnail_image sub-type routing target
1471
+ summary: Generate a thumbnail for an image using the new thumbnail_image sub-type routing target. The SNS attribute `operation_type` carries the sub-type (drives routing); the payload `operation_type` carries the base value (`thumbnail`).
992
1472
  headers:
993
1473
  operation_type: "thumbnail_image"
994
1474
  media_group: "image"
995
1475
  payload:
996
1476
  job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e121"
997
1477
  operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e221"
998
- operation_type: "thumbnail_image"
1478
+ operation_type: "thumbnail"
999
1479
  file_type: "image/png"
1000
1480
  source_bucket: "gisl-stg-euw1-input"
1001
1481
  source_key: "uploads/018f9c42-kkll/screenshot.png"
@@ -1006,47 +1486,52 @@ components:
1006
1486
  height: 512
1007
1487
  fit: "max"
1008
1488
  format: "webp"
1009
- - name: Image Watermark (image overlay)
1010
- summary: Apply a PNG logo overlay to a JPEG image
1489
+ - name: Image Watermark (file overlay)
1490
+ summary: Apply a PNG logo overlay to a JPEG image (V2 multi-input Path B)
1011
1491
  headers:
1012
- operation_type: "watermark"
1492
+ operation_type: "image_watermark"
1013
1493
  media_group: "image"
1014
1494
  payload:
1015
1495
  job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e118"
1016
1496
  operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e210"
1017
- operation_type: "watermark"
1497
+ operation_type: "image_watermark"
1018
1498
  file_type: "image/jpeg"
1019
- source_bucket: "gisl-stg-euw1-input"
1020
- source_key: "uploads/018f9c42-eeff/photo.jpg"
1499
+ sources:
1500
+ - role: "base"
1501
+ source_bucket: "gisl-stg-euw1-input"
1502
+ source_key: "uploads/018f9c42-eeff/photo.jpg"
1503
+ - role: "overlay"
1504
+ source_bucket: "gisl-stg-euw1-assets"
1505
+ source_key: "brand/logo.png"
1021
1506
  output_bucket: "gisl-stg-euw1-output"
1022
1507
  output_key_prefix: "jobs/018f9c42-5d6b-7481-b3df-9fd0a0a5e118/018f9c42-5d6b-7481-b3df-9fd0a0a5e210/"
1023
1508
  options:
1024
- watermark_type: "image"
1025
- watermark_bucket: "gisl-stg-euw1-assets"
1026
- watermark_key: "brand/logo.png"
1027
- position: "bottom-right"
1509
+ anchor: "bottom_right"
1510
+ margin_x: "20px"
1511
+ margin_y: "20px"
1028
1512
  opacity: 0.6
1029
- - name: Image Watermark (tiled text overlay)
1030
- summary: Render a rotated tiled text watermark across a PNG image
1513
+ overlay_width: "200px"
1514
+ - name: Text Watermark (tiled)
1515
+ summary: Render a rotated tiled text watermark across a PNG image (V2 single-input)
1031
1516
  headers:
1032
- operation_type: "watermark"
1517
+ operation_type: "text_watermark"
1033
1518
  media_group: "image"
1034
1519
  payload:
1035
1520
  job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e119"
1036
1521
  operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e211"
1037
- operation_type: "watermark"
1522
+ operation_type: "text_watermark"
1038
1523
  file_type: "image/png"
1039
1524
  source_bucket: "gisl-stg-euw1-input"
1040
1525
  source_key: "uploads/018f9c42-gghh/screenshot.png"
1041
1526
  output_bucket: "gisl-stg-euw1-output"
1042
1527
  output_key_prefix: "jobs/018f9c42-5d6b-7481-b3df-9fd0a0a5e119/018f9c42-5d6b-7481-b3df-9fd0a0a5e211/"
1043
1528
  options:
1044
- watermark_type: "text"
1045
- watermark_mode: "tiled"
1046
1529
  text: "CONFIDENTIAL"
1047
1530
  font_size: 64
1048
1531
  color: "#FF000080"
1532
+ font_family: "liberation_sans"
1049
1533
  rotation: -45
1534
+ watermark_mode: "tiled"
1050
1535
  tile_spacing: 120
1051
1536
  opacity: 0.4
1052
1537
  - name: Image Convert
@@ -1113,25 +1598,6 @@ components:
1113
1598
  options:
1114
1599
  frame_delay: 100
1115
1600
  loop: true
1116
- - name: PDF Merge
1117
- summary: Concatenate multiple PDFs into one
1118
- headers:
1119
- operation_type: "merge"
1120
- media_group: "document"
1121
- payload:
1122
- job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e116"
1123
- operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e204"
1124
- operation_type: "merge"
1125
- file_type: "application/pdf"
1126
- output_type: "document"
1127
- sources:
1128
- - bucket: "gisl-stg-euw1-output"
1129
- key: "jobs/018f9c42-aaa1/018f9c42-bbb1/chapter1.pdf"
1130
- - bucket: "gisl-stg-euw1-output"
1131
- key: "jobs/018f9c42-aaa2/018f9c42-bbb2/chapter2.pdf"
1132
- output_bucket: "gisl-stg-euw1-output"
1133
- output_key_prefix: "jobs/018f9c42-5d6b-7481-b3df-9fd0a0a5e116/018f9c42-5d6b-7481-b3df-9fd0a0a5e204/"
1134
- options: {}
1135
1601
  - name: Archive
1136
1602
  summary: Bundle multiple files into a ZIP archive (no media_group attribute — archive is media-agnostic)
1137
1603
  headers:
@@ -1216,6 +1682,157 @@ components:
1216
1682
  # OPERATION RESULT MESSAGE
1217
1683
  # ============================================
1218
1684
 
1685
+ # ============================================
1686
+ # UPLOAD-PROBE MESSAGES (per ticket vBlEurU7)
1687
+ # ============================================
1688
+
1689
+ UploadProbeRequestMessage:
1690
+ name: UploadProbeRequest
1691
+ title: Upload Probe Request
1692
+ summary: Request for the upload-probe Lambda to analyse an uploaded file
1693
+ description: |
1694
+ Request published by the API to the upload-probe Lambda. The
1695
+ same payload shape is also used as the Lambda invoke payload
1696
+ on the synchronous `POST /api/uploads/{id}/probe` path.
1697
+
1698
+ **Invocation paths:**
1699
+ - **Sync (Lambda RequestResponse)** — backs
1700
+ `POST /api/uploads/{id}/probe`. The API Lambda invoker
1701
+ serialises this payload into the Invoke event body and
1702
+ returns the Lambda response (`UploadProbeResponse` from
1703
+ `openapi/api.yaml`) inline.
1704
+ - **Async (SNS -> SQS)** — published to the
1705
+ `upload-probe-requests` topic on upload finalize for eager
1706
+ `rich_metadata` enrichment. The Lambda consumes from the
1707
+ subscribed queue and publishes a completion to the
1708
+ `upload-probe-completions` queue.
1709
+
1710
+ The Lambda differentiates the two paths from the AWS event
1711
+ source (Lambda RequestResponse vs SNS event) — no flag in
1712
+ the payload distinguishes them.
1713
+ contentType: application/json
1714
+ payload:
1715
+ $ref: '#/components/schemas/UploadProbeRequest'
1716
+ examples:
1717
+ - name: Async upload-finalize probe
1718
+ summary: Eager enrichment for a long-form video upload
1719
+ payload:
1720
+ file_id: "019539ab-1111-7000-8000-000000000010"
1721
+ source_bucket: "gisl-stg-euw1-input"
1722
+ source_key: "uploads/019539ab-1111-7000-8000-000000000010/clip.mp4"
1723
+ file_type: "video/mp4"
1724
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000010"
1725
+ requested_at: "2026-05-06T13:50:00Z"
1726
+ - name: Sync preflight probe
1727
+ summary: Caller-initiated preflight via POST /api/uploads/{id}/probe
1728
+ payload:
1729
+ file_id: "019539ab-1111-7000-8000-000000000011"
1730
+ source_bucket: "gisl-stg-euw1-input"
1731
+ source_key: "uploads/019539ab-1111-7000-8000-000000000011/clip.mp4"
1732
+ file_type: "video/mp4"
1733
+ idempotency_key: "019539ab-1111-7000-8000-000000000011"
1734
+ requested_at: "2026-05-06T13:50:00Z"
1735
+
1736
+ UploadProbeCompletionMessage:
1737
+ name: UploadProbeCompletion
1738
+ title: Upload Probe Completion
1739
+ summary: Completion notification from the upload-probe Lambda (async branch only)
1740
+ description: |
1741
+ Published by the upload-probe Lambda to the
1742
+ `upload-probe-completions` queue when the async branch
1743
+ finishes. The payload carries either a successful probe
1744
+ result (parity with `UploadProbeResponse` from
1745
+ `openapi/api.yaml`) or a failure envelope (`error_code` +
1746
+ `error_message`).
1747
+
1748
+ **Idempotency.** `idempotency_key` is echoed from the
1749
+ request so the API consumer can deduplicate against its
1750
+ upload row's persisted probe state.
1751
+
1752
+ Sync invocations do not publish here — sync results are
1753
+ returned inline as `UploadProbeResponse`.
1754
+ contentType: application/json
1755
+ payload:
1756
+ $ref: '#/components/schemas/UploadProbeCompletion'
1757
+ examples:
1758
+ - name: Successful probe (long-form video with audio track)
1759
+ summary: Lambda extracted full media metadata for a 4K H.265 video
1760
+ payload:
1761
+ file_id: "019539ab-1111-7000-8000-000000000010"
1762
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000010"
1763
+ probe_status: "ok"
1764
+ processing_class_pre_assignment: "long_form"
1765
+ media_metadata:
1766
+ duration_seconds: 5400
1767
+ width: 3840
1768
+ height: 2160
1769
+ codec: "h265"
1770
+ audio_codec: "aac"
1771
+ container: "mp4"
1772
+ fps: 23.976
1773
+ bitrate_bps: 18000000
1774
+ audio_layout: "stereo"
1775
+ channels: 2
1776
+ sample_rate_hz: 48000
1777
+ probed_at: "2026-05-06T13:50:01Z"
1778
+ probed_at: "2026-05-06T13:50:01Z"
1779
+ probe_version: "1.0.0"
1780
+ - name: Successful probe (audio-only)
1781
+ summary: MP3 podcast episode — codec under `codec`, no `audio_codec` field
1782
+ payload:
1783
+ file_id: "019539ab-1111-7000-8000-000000000020"
1784
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000020"
1785
+ probe_status: "ok"
1786
+ processing_class_pre_assignment: "short_form"
1787
+ media_metadata:
1788
+ duration_seconds: 2700
1789
+ codec: "mp3"
1790
+ container: "mp3"
1791
+ bitrate_bps: 192000
1792
+ audio_layout: "stereo"
1793
+ channels: 2
1794
+ sample_rate_hz: 44100
1795
+ probed_at: "2026-05-06T13:50:01Z"
1796
+ probed_at: "2026-05-06T13:50:01Z"
1797
+ probe_version: "1.0.0"
1798
+ - name: Successful probe (document)
1799
+ summary: PDF — `page_count` + `dpi` populated, video/audio fields omitted
1800
+ payload:
1801
+ file_id: "019539ab-1111-7000-8000-000000000021"
1802
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000021"
1803
+ probe_status: "ok"
1804
+ processing_class_pre_assignment: "short_form"
1805
+ media_metadata:
1806
+ container: "pdf"
1807
+ page_count: 42
1808
+ dpi: 300
1809
+ probed_at: "2026-05-06T13:50:01Z"
1810
+ probed_at: "2026-05-06T13:50:01Z"
1811
+ probe_version: "1.0.0"
1812
+ - name: Probe surfaced unsupported codec
1813
+ summary: Container readable; codec not in supported set
1814
+ payload:
1815
+ file_id: "019539ab-1111-7000-8000-000000000012"
1816
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000012"
1817
+ probe_status: "unsupported_codec"
1818
+ processing_class_pre_assignment: "blocked"
1819
+ media_metadata:
1820
+ duration_seconds: 600
1821
+ codec: "prores"
1822
+ container: "mov"
1823
+ probed_at: "2026-05-06T13:50:01Z"
1824
+ probed_at: "2026-05-06T13:50:01Z"
1825
+ probe_version: "1.0.0"
1826
+ - name: Probe failed (Lambda-side error)
1827
+ summary: S3 download failed; probe could not run
1828
+ payload:
1829
+ file_id: "019539ab-1111-7000-8000-000000000013"
1830
+ idempotency_key: "upload-finalize:019539ab-1111-7000-8000-000000000013"
1831
+ error_code: "s3_download_failed"
1832
+ error_message: "Failed to download source file from S3"
1833
+ probed_at: "2026-05-06T13:50:01Z"
1834
+ probe_version: "1.0.0"
1835
+
1219
1836
  OperationResultMessage:
1220
1837
  name: OperationResult
1221
1838
  title: Operation Result
@@ -1290,8 +1907,148 @@ components:
1290
1907
  error_message: "Failed to download source file from S3"
1291
1908
  is_retryable: true
1292
1909
  last_progress: 0
1910
+ - name: Successful Multi-Output Convert (PDF to PNG, 3 pages)
1911
+ summary: Convert PDF->image fan-out result per ADR-0009
1912
+ payload:
1913
+ job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e115"
1914
+ operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e203"
1915
+ operation_type: "convert"
1916
+ status: "completed"
1917
+ output_bucket: "gisl-stg-euw1-output"
1918
+ total_output_size_bytes: 786432
1919
+ outputs:
1920
+ - output_key: "jobs/018f9c42-5d6b/018f9c42-5d6b-e203/page-001.png"
1921
+ output_size_bytes: 262144
1922
+ page_index: 1
1923
+ - output_key: "jobs/018f9c42-5d6b/018f9c42-5d6b-e203/page-002.png"
1924
+ output_size_bytes: 262144
1925
+ page_index: 2
1926
+ - output_key: "jobs/018f9c42-5d6b/018f9c42-5d6b-e203/page-003.png"
1927
+ output_size_bytes: 262144
1928
+ page_index: 3
1929
+ metrics:
1930
+ input_count: 1
1931
+ output_size_bytes: 786432
1932
+ duration_ms: 2300
1933
+ - name: Failed Multi-Output Convert (page 2 of 3 corrupted)
1934
+ summary: |
1935
+ Multi-output operation failed mid-stream per ADR-0009 §D3 (all-or-nothing
1936
+ failure). Lambda short-circuited on page 2; page 1 was uploaded before
1937
+ the failure, listed in outputs[] as informational data only — consumers
1938
+ MUST NOT treat them as usable on failure.
1939
+ payload:
1940
+ job_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e115"
1941
+ operation_id: "018f9c42-5d6b-7481-b3df-9fd0a0a5e203"
1942
+ operation_type: "convert"
1943
+ status: "failed"
1944
+ error_code: "processing_failed"
1945
+ error_message: "pdftocairo failed on page 2: invalid page tree node"
1946
+ is_retryable: false
1947
+ last_progress: 50
1948
+ outputs:
1949
+ - output_key: "jobs/018f9c42-5d6b/018f9c42-5d6b-e203/page-001.png"
1950
+ output_size_bytes: 262144
1951
+ page_index: 1
1293
1952
 
1294
1953
  schemas:
1954
+ # ============================================
1955
+ # AVAILABILITY METADATA (decorative)
1956
+ # ============================================
1957
+
1958
+ AvailabilityValue:
1959
+ type: string
1960
+ description: |
1961
+ Availability level for an operation, mime_group, option, or
1962
+ per-enum-value entry per ADR-0001 §1.3. Mirrors the
1963
+ `AvailabilityValue` schema in `openapi/api.yaml` so SDK
1964
+ generators that consume both specs emit a single typed binding.
1965
+
1966
+ Used as the value of decorative `x-availability` extensions in
1967
+ this spec, and as the typed schema future tickets (I17
1968
+ `per_value_availability` primitive; I27 phased progress states)
1969
+ reference for availability metadata on event payloads.
1970
+
1971
+ Per ADR-0001 §1.4: when `availability` is absent, the implicit
1972
+ value is `stable` for consumers. `stable_pending_audit` is
1973
+ internal CI accounting only and never surfaces.
1974
+
1975
+ See `schemas/FORMAT.md` §Availability Taxonomy for full rules.
1976
+ enum:
1977
+ - stable
1978
+ - beta
1979
+ - experimental
1980
+ - planned
1981
+ - deprecated
1982
+
1983
+ PerValueAvailabilityEntry:
1984
+ type: object
1985
+ description: |
1986
+ Single per-enum-value availability entry. Mirrors
1987
+ `PerValueAvailabilityEntry` in `openapi/api.yaml` so SDK
1988
+ generators consuming both specs emit a single typed binding.
1989
+
1990
+ AsyncAPI consumers: this primitive is the canonical mechanism
1991
+ for tagging individual enum values at different availability
1992
+ levels — see `schemas/operations/merge.yaml` `transition`
1993
+ (per ticket I16-CONS) and `audio_watermark.method` /
1994
+ `audio_overlay.mode` for shipped consumers. The
1995
+ `ProgressStatus` enum (probing / decoding / encoding values
1996
+ landed via ticket I27) deliberately does NOT use
1997
+ `per_value_availability` — those phase statuses are
1998
+ long-form-operation conditional rather than caller-facing
1999
+ feature gates, and their visibility is controlled by which
2000
+ operations emit them rather than by per-value availability
2001
+ tags.
2002
+
2003
+ See `schemas/FORMAT.md` §Availability Taxonomy for full rules.
2004
+ required:
2005
+ - availability
2006
+ properties:
2007
+ availability:
2008
+ $ref: '#/components/schemas/AvailabilityValue'
2009
+ required_tier:
2010
+ description: Tier required to use this enum value. Optional.
2011
+ type: string
2012
+ enum:
2013
+ - free
2014
+ - pro
2015
+ - enterprise
2016
+ eta:
2017
+ type: string
2018
+ description: |
2019
+ ISO-8601 date or quarter (`2026-Q3`) when this value is
2020
+ expected to ship. Only meaningful for `availability: planned`.
2021
+ documentation_url:
2022
+ type: string
2023
+ format: uri
2024
+ sunset:
2025
+ type: string
2026
+ format: date
2027
+
2028
+ PerValueAvailability:
2029
+ type: object
2030
+ description: |
2031
+ Map of enum-value → `PerValueAvailabilityEntry`. Mirrors the
2032
+ OpenAPI version. Keys MUST be a subset of the option / status
2033
+ enum's `values[]` (or AsyncAPI message field's `enum`) array.
2034
+ additionalProperties:
2035
+ $ref: '#/components/schemas/PerValueAvailabilityEntry'
2036
+
2037
+ PerMimeAvailability:
2038
+ type: object
2039
+ description: |
2040
+ Map of MIME-type → `PerValueAvailabilityEntry`. Mirrors the
2041
+ OpenAPI `PerMimeAvailability` schema so SDK generators
2042
+ consuming both specs emit a single typed binding.
2043
+
2044
+ Keys MUST be a subset of the parent
2045
+ `mime_groups.<group>.mimes` array (CI-checked by
2046
+ `scripts/check-per-mime-availability.py`). Absent keys default
2047
+ to `availability: stable` per ADR-0001 §1.4. Per ticket
2048
+ [`YXYOo6gg`](https://trello.com/c/YXYOo6gg).
2049
+ additionalProperties:
2050
+ $ref: '#/components/schemas/PerValueAvailabilityEntry'
2051
+
1295
2052
  # ============================================
1296
2053
  # FIFO MESSAGE HEADERS
1297
2054
  # ============================================
@@ -1374,8 +2131,35 @@ components:
1374
2131
  - `operation_type` is always present.
1375
2132
  - `media_group` is required iff `operation_type != archive`.
1376
2133
  Archive is explicitly omitted because it is media-agnostic.
1377
- - For `operation_type == watermark`, `media_group` must equal
1378
- `image` (watermark is image-only).
2134
+ - For `operation_type = text_watermark`, `media_group` must
2135
+ equal `image` (text_watermark is image-only).
2136
+ - For `operation_type = custom_luma`, `media_group` must
2137
+ equal `video` (custom_luma applies a luma matte to a video
2138
+ base; the operation is `availability: planned` per
2139
+ [I29](https://trello.com/c/EPUE5Vs1) so this constraint is
2140
+ contract-defined but not exercised today).
2141
+ - For `operation_type = audio_overlay`, `media_group` must
2142
+ equal `audio` or `video` (audio_overlay supports both —
2143
+ mixing into an audio track or into a video's audio track).
2144
+ Both mime_groups are `availability: planned` per
2145
+ [I19](https://trello.com/c/Xr3Z4GBF), so this constraint is
2146
+ contract-defined but not exercised today.
2147
+ - For `operation_type = audio_watermark`, `media_group` must
2148
+ equal `audio` or `video` (audio_watermark supports both —
2149
+ embedding into an audio asset or into a video's audio
2150
+ track). Both mime_groups are `availability: planned` +
2151
+ `required_tier: enterprise` per
2152
+ [I20](https://trello.com/c/omiCq7Vn), so this constraint is
2153
+ contract-defined but not exercised today.
2154
+ - For `operation_type = image_watermark`, `media_group` must
2155
+ equal `image` while only the stable `image` mime_group is
2156
+ dispatchable. The schema declares `image_gif` and `video`
2157
+ mime_groups at `availability: planned` (I5 — Trello AKZiOXnd);
2158
+ when those flip to `stable`, this constraint widens to
2159
+ `{image, video}` (animated GIF carries `media_group=image`).
2160
+ Until then the API rejects non-image bases at workflow-create
2161
+ time with `feature_not_available` (422), so SNS-attribute
2162
+ values stay constrained to `image`.
1379
2163
  - For the four thumbnail sub-types, `media_group` is constrained
1380
2164
  to the matching media:
1381
2165
  - `thumbnail_image` -> `image`
@@ -1398,10 +2182,14 @@ components:
1398
2182
  - thumbnail_video
1399
2183
  - thumbnail_document
1400
2184
  - thumbnail_office
1401
- - watermark
2185
+ - image_watermark
2186
+ - text_watermark
1402
2187
  - merge
1403
2188
  - archive
1404
2189
  - convert
2190
+ - custom_luma
2191
+ - audio_overlay
2192
+ - audio_watermark
1405
2193
  media_group:
1406
2194
  type: string
1407
2195
  description: |
@@ -1429,11 +2217,43 @@ components:
1429
2217
  - if:
1430
2218
  properties:
1431
2219
  operation_type:
1432
- const: watermark
2220
+ const: image_watermark
2221
+ then:
2222
+ properties:
2223
+ media_group:
2224
+ const: image
2225
+ - if:
2226
+ properties:
2227
+ operation_type:
2228
+ const: text_watermark
1433
2229
  then:
1434
2230
  properties:
1435
2231
  media_group:
1436
2232
  const: image
2233
+ - if:
2234
+ properties:
2235
+ operation_type:
2236
+ const: custom_luma
2237
+ then:
2238
+ properties:
2239
+ media_group:
2240
+ const: video
2241
+ - if:
2242
+ properties:
2243
+ operation_type:
2244
+ const: audio_overlay
2245
+ then:
2246
+ properties:
2247
+ media_group:
2248
+ enum: [audio, video]
2249
+ - if:
2250
+ properties:
2251
+ operation_type:
2252
+ const: audio_watermark
2253
+ then:
2254
+ properties:
2255
+ media_group:
2256
+ enum: [audio, video]
1437
2257
  - if:
1438
2258
  properties:
1439
2259
  operation_type:
@@ -1479,15 +2299,17 @@ components:
1479
2299
  `OperationRequestMessage` (non-compression branch). Published by
1480
2300
  API to SNS, consumed by Lambda from SQS.
1481
2301
 
1482
- **Single-input operations** (compress, thumbnail, thumbnail_image,
1483
- thumbnail_video, thumbnail_document, thumbnail_office, watermark,
2302
+ **Single-input operations** (compress, thumbnail, text_watermark,
1484
2303
  convert): use `source_bucket` + `source_key` for the input file.
1485
2304
  `file_type` is required.
1486
2305
 
1487
- **Multi-input operations** (merge, archive): use `sources` array.
1488
- Each entry has `bucket` + `key`. Merge entries may include
1489
- per-input overrides (e.g. transition type). Merge also requires
1490
- `output_type`.
2306
+ **Multi-input operations** (merge, archive, image_watermark,
2307
+ custom_luma, audio_overlay): use `sources` array. Each entry
2308
+ has `bucket` + `key`. Merge entries may include per-input
2309
+ overrides (e.g. transition type). Merge also requires
2310
+ `output_type`. custom_luma and audio_overlay are
2311
+ `availability: planned` so workflow-create rejects with 422
2312
+ today; the wire shape is contract-defined.
1491
2313
 
1492
2314
  Note: `media_group` is **not** a field in this payload. It exists
1493
2315
  only as an SNS MessageAttribute on the non-compression branch; on
@@ -1506,12 +2328,9 @@ components:
1506
2328
  enum:
1507
2329
  - compress
1508
2330
  - thumbnail
1509
- - thumbnail_image
1510
- - thumbnail_video
1511
- - thumbnail_document
1512
- - thumbnail_office
1513
- - watermark
2331
+ - text_watermark
1514
2332
  - convert
2333
+ - audio_watermark
1515
2334
  then:
1516
2335
  required:
1517
2336
  - file_type
@@ -1520,7 +2339,7 @@ components:
1520
2339
  - if:
1521
2340
  properties:
1522
2341
  operation_type:
1523
- enum: [merge, archive]
2342
+ enum: [merge, archive, image_watermark, custom_luma, audio_overlay]
1524
2343
  then:
1525
2344
  required:
1526
2345
  - sources
@@ -1584,9 +2403,14 @@ components:
1584
2403
  sources:
1585
2404
  type: array
1586
2405
  description: |
1587
- Input files for multi-input operations (merge, archive).
1588
- Each entry specifies an S3 location. Order matters for merge (concatenation order).
1589
- Per-input overrides (e.g. transition) can be inlined on each entry.
2406
+ Input files for multi-input operations (merge, archive,
2407
+ image_watermark, custom_luma, audio_overlay). Each entry
2408
+ specifies an S3 location. Order matters for merge
2409
+ (concatenation order). Per-input overrides (e.g.
2410
+ transition) can be inlined on each entry.
2411
+ Role-based multi-input operations (image_watermark,
2412
+ custom_luma, audio_overlay) require a `role` discriminator
2413
+ on each entry — see `SourceEntry.role`.
1590
2414
  items:
1591
2415
  $ref: '#/components/schemas/SourceEntry'
1592
2416
  minItems: 2
@@ -1633,8 +2457,11 @@ components:
1633
2457
  SourceEntry:
1634
2458
  type: object
1635
2459
  description: |
1636
- A single source file for multi-input operations (merge, archive).
1637
- Provides the S3 location and optional per-input overrides.
2460
+ A single source file for multi-input operations (merge,
2461
+ archive, image_watermark, custom_luma, audio_overlay).
2462
+ Provides the S3 location, optional per-input overrides, and
2463
+ the `role` discriminator for role-based multi-input
2464
+ operations.
1638
2465
  required:
1639
2466
  - bucket
1640
2467
  - key
@@ -1647,18 +2474,16 @@ components:
1647
2474
  type: string
1648
2475
  description: S3 key of the source file
1649
2476
  example: "jobs/018f9c42-aaa1/018f9c42-bbb1/intro.mp4"
2477
+ role:
2478
+ # Named schema preserves the per-operation role rules in its
2479
+ # description; lifted from inline per ticket QokFw8VR.
2480
+ $ref: '#/components/schemas/JobInputRole'
1650
2481
  transition:
1651
2482
  type: string
1652
2483
  description: |
1653
2484
  Per-input transition override for video merge.
1654
2485
  Overrides the global transition option for the join point before this input.
1655
2486
  example: "none"
1656
- page_range:
1657
- type: string
1658
- description: |
1659
- Page range for PDF merge. Selects specific pages from this input.
1660
- Format: "1-5", "1,3,5-10". Omit for all pages.
1661
- example: "1-5"
1662
2487
 
1663
2488
  # ============================================
1664
2489
  # OPERATION PROGRESS SCHEMA
@@ -1704,6 +2529,30 @@ components:
1704
2529
  type: string
1705
2530
  description: Human-readable description of current processing stage
1706
2531
  example: "Compressing image"
2532
+ phase_input_index:
2533
+ type: integer
2534
+ minimum: 1
2535
+ description: |
2536
+ 1-based index of the input currently being processed in
2537
+ this phase. Emitted only when `status` is `probing` or
2538
+ `decoding` (and optionally `encoding` for multi-output
2539
+ operations) to give callers visibility into long-form
2540
+ jobs that internally process inputs sequentially.
2541
+ Frontend renders "probing input 2/4" / "decoding input
2542
+ 3/4" without callers having to decompose into multiple
2543
+ jobs. Per ticket I27 (Trello 1R3K3bsG) + plan v5 §F8.3.
2544
+ example: 2
2545
+ phase_total_inputs:
2546
+ type: integer
2547
+ minimum: 1
2548
+ description: |
2549
+ Total number of inputs expected in this phase. Pairs with
2550
+ `phase_input_index`. For single-input operations (most
2551
+ ops) `phase_total_inputs: 1` and `phase_input_index: 1`
2552
+ during the relevant phase. For multi-input merge,
2553
+ archive, image_watermark, custom_luma, and audio_overlay,
2554
+ this matches the inputs[] count. Per ticket I27.
2555
+ example: 4
1707
2556
 
1708
2557
  # ============================================
1709
2558
  # OPERATION RESULT SCHEMA
@@ -1717,25 +2566,81 @@ components:
1717
2566
 
1718
2567
  **Important**: This message MUST always be sent, whether the operation
1719
2568
  succeeds or fails. It is the definitive signal that processing is complete.
2569
+
2570
+ ## Single-output vs multi-output completion
2571
+
2572
+ Per [ADR-0009](../docs/decisions/0009-multi-output-result-envelope.md),
2573
+ successful completion uses one of two mutually-exclusive shapes:
2574
+
2575
+ - **Single-output** (compress, watermark, merge, archive, convert
2576
+ single-output, thumbnail): top-level `output_key` + `output_size_bytes`
2577
+ are present; `outputs[]` is absent.
2578
+ - **Multi-output** (convert PDF->image; future fan-out operations):
2579
+ `outputs[]` + `total_output_size_bytes` are present; top-level
2580
+ `output_key` / `output_size_bytes` are absent. Consumers MUST use
2581
+ `outputs.length` (or its language equivalent) for the count; per
2582
+ ADR-0009 §D4 a denormalised `output_count` field was deliberately
2583
+ rejected.
2584
+
2585
+ `output_bucket` is shared across both shapes — all outputs in a
2586
+ multi-output operation live in the same bucket.
2587
+
2588
+ Failure (`status: failed`) is unified across both shapes: one
2589
+ `error_code` + `error_message` + `is_retryable` at the top level; no
2590
+ per-output error array (all-or-nothing failure per ADR-0009 §D3).
2591
+ `outputs[]` MAY be present on failure as informational data showing
2592
+ which outputs completed before the failing one; consumers MUST NOT
2593
+ treat them as usable.
1720
2594
  required:
1721
2595
  - job_id
1722
2596
  - operation_id
1723
2597
  - operation_type
1724
2598
  - status
1725
- if:
1726
- properties:
1727
- status:
1728
- const: completed
1729
- then:
1730
- required:
1731
- - output_bucket
1732
- - output_key
1733
- - output_size_bytes
1734
- else:
1735
- required:
1736
- - error_code
1737
- - error_message
1738
- - is_retryable
2599
+ oneOf:
2600
+ - title: SingleOutputCompletion
2601
+ description: |
2602
+ Legacy single-output completion. Used by compress, watermark,
2603
+ merge, archive, convert single-output, and thumbnail operations.
2604
+ properties:
2605
+ status:
2606
+ const: completed
2607
+ required:
2608
+ - output_bucket
2609
+ - output_key
2610
+ - output_size_bytes
2611
+ not:
2612
+ anyOf:
2613
+ - required: [outputs]
2614
+ - required: [output_count]
2615
+ - required: [total_output_size_bytes]
2616
+ - title: MultiOutputCompletion
2617
+ description: |
2618
+ Multi-output completion. Used by convert PDF->image and future
2619
+ fan-out operations. Per ADR-0009.
2620
+ properties:
2621
+ status:
2622
+ const: completed
2623
+ required:
2624
+ - output_bucket
2625
+ - outputs
2626
+ - total_output_size_bytes
2627
+ not:
2628
+ anyOf:
2629
+ - required: [output_key]
2630
+ - required: [output_size_bytes]
2631
+ - title: Failure
2632
+ description: |
2633
+ Operation failed (single- or multi-output). One top-level error
2634
+ envelope per ADR-0009 §D3 (all-or-nothing failure). `outputs[]`
2635
+ MAY be present with partial results as informational data; do
2636
+ not consume.
2637
+ properties:
2638
+ status:
2639
+ const: failed
2640
+ required:
2641
+ - error_code
2642
+ - error_message
2643
+ - is_retryable
1739
2644
  properties:
1740
2645
  job_id:
1741
2646
  type: string
@@ -1752,20 +2657,75 @@ components:
1752
2657
  status:
1753
2658
  $ref: '#/components/schemas/ResultStatus'
1754
2659
 
1755
- # Success fields
2660
+ # Shared success field (both single- and multi-output)
1756
2661
  output_bucket:
1757
2662
  type: string
1758
- description: S3 bucket where output file is stored (completed only)
2663
+ description: |
2664
+ S3 bucket where output file(s) are stored. Present on completion
2665
+ for both single- and multi-output operations; all outputs in a
2666
+ multi-output operation share this bucket. Absent on failure.
1759
2667
  example: "gisl-stg-euw1-output"
2668
+
2669
+ # Single-output success fields (legacy; per ADR-0009 SingleOutputCompletion)
1760
2670
  output_key:
1761
2671
  type: string
1762
- description: S3 key of the output file (completed only)
2672
+ description: |
2673
+ S3 key of the output file. Single-output completion only — mutually
2674
+ exclusive with `outputs[]`. Absent for multi-output operations and
2675
+ on failure.
1763
2676
  example: "jobs/018f9c42-5d6b/018f9c42-5d6b-e200/photo_compressed.png"
1764
2677
  output_size_bytes:
1765
2678
  type: integer
1766
2679
  format: int64
1767
- description: Output file size in bytes (completed only)
2680
+ description: |
2681
+ Output file size in bytes. Single-output completion only — mutually
2682
+ exclusive with `outputs[]`. Absent for multi-output operations and
2683
+ on failure.
1768
2684
  example: 2097152
2685
+
2686
+ # Multi-output success fields (per ADR-0009 MultiOutputCompletion)
2687
+ outputs:
2688
+ type: array
2689
+ minItems: 1
2690
+ maxItems: 200
2691
+ description: |
2692
+ Per-output details for multi-output operations (e.g. convert
2693
+ PDF->image emits one entry per page). Mutually exclusive with
2694
+ top-level `output_key` / `output_size_bytes` on completion. MAY
2695
+ be present on failure as informational data showing partial
2696
+ results; consumers MUST NOT treat those as usable. See ADR-0009.
2697
+
2698
+ **`maxItems: 200`** is a worst-case transport-layer defence
2699
+ against SQS/SNS's 256 KiB payload cap. Worst-case per-entry size
2700
+ is ~1080 bytes (1024-char `output_key` at the S3 hard limit + ~55
2701
+ bytes JSON envelope per entry); 200 entries × 1080 bytes ≈ 216
2702
+ KB, leaving ~40 KB headroom for the outer message envelope
2703
+ (`job_id`, `operation_id`, `metrics`, `total_output_size_bytes`,
2704
+ etc.). Typical Lambda-generated keys are ~110 chars so the
2705
+ realistic per-message size stays well under the cap; this bound
2706
+ defends against the worst case, not the typical case.
2707
+
2708
+ Operations that could theoretically exceed 200 outputs from one
2709
+ logical operation (e.g. convert PDF->image on a >200-page PDF)
2710
+ MUST enforce a corresponding upper bound on the input side
2711
+ (per-operation page-range / count option in the operation's
2712
+ `schemas/operations/*.yaml`); the transport bound is defence in
2713
+ depth, not the primary user-facing limit. See ADR-0009 §D5.
2714
+ items:
2715
+ $ref: '#/components/schemas/OperationResultOutputEntry'
2716
+ total_output_size_bytes:
2717
+ type: integer
2718
+ format: int64
2719
+ minimum: 0
2720
+ description: |
2721
+ Aggregate size of all `outputs[]` in bytes. Equals
2722
+ `sum(outputs[].output_size_bytes)`. Emitted only with multi-output
2723
+ completion. Used for billing and aggregate observability. Per
2724
+ ADR-0009 §D4, this is denormalised against the per-entry sum;
2725
+ consumers SHOULD trust the per-entry sum if the two disagree (and
2726
+ log a warning).
2727
+ example: 786432
2728
+
1769
2729
  metrics:
1770
2730
  $ref: '#/components/schemas/OperationMetrics'
1771
2731
 
@@ -1787,6 +2747,156 @@ components:
1787
2747
  description: Progress percentage when the operation failed (failed only)
1788
2748
  example: 10
1789
2749
 
2750
+ OperationResultOutputEntry:
2751
+ type: object
2752
+ description: |
2753
+ Single entry in the `OperationResult.outputs[]` array for multi-output
2754
+ operations. Per ADR-0009.
2755
+
2756
+ Each entry represents one output file produced by the operation, with
2757
+ its S3 key, size, and an indexing field (`page_index` for PDF-page
2758
+ outputs, `position` for generic ordinals). Per ADR-0009 §D2, an
2759
+ operation uses **one** indexing field consistently for all its
2760
+ outputs; the two are mutually exclusive within an entry.
2761
+ required:
2762
+ - output_key
2763
+ - output_size_bytes
2764
+ oneOf:
2765
+ - title: PageIndexed
2766
+ description: PDF-page output (convert PDF->image).
2767
+ required: [page_index]
2768
+ not: { required: [position] }
2769
+ - title: PositionIndexed
2770
+ description: Generic ordinal output (frame strip, chapter split).
2771
+ required: [position]
2772
+ not: { required: [page_index] }
2773
+ - title: Unindexed
2774
+ description: |
2775
+ Output without an explicit indexing field. Reserved for future
2776
+ operations that index by something other than page/position.
2777
+ Schema-valid but not currently emitted by any operation.
2778
+ not:
2779
+ anyOf:
2780
+ - required: [page_index]
2781
+ - required: [position]
2782
+ properties:
2783
+ output_key:
2784
+ type: string
2785
+ maxLength: 1024
2786
+ description: |
2787
+ S3 key of this individual output file. `maxLength: 1024` matches
2788
+ the S3 object key hard limit; keys longer than this are rejected
2789
+ by S3 itself, so this is a contract-level mirror of the upstream
2790
+ constraint.
2791
+ example: "jobs/018f9c42-5d6b/018f9c42-5d6b-e200/page-001.png"
2792
+ output_size_bytes:
2793
+ type: integer
2794
+ format: int64
2795
+ minimum: 0
2796
+ description: Size of this individual output file in bytes.
2797
+ example: 262144
2798
+ page_index:
2799
+ type: integer
2800
+ minimum: 1
2801
+ description: |
2802
+ 1-based page number for PDF-page outputs. Gapless within an
2803
+ operation (an N-page conversion emits `page_index` 1..N). Mutually
2804
+ exclusive with `position`. Per ADR-0009 §D2.
2805
+ example: 1
2806
+ position:
2807
+ type: integer
2808
+ minimum: 0
2809
+ description: |
2810
+ 0-based ordinal for non-PDF multi-output operations (e.g. frame
2811
+ strip, chapter split). Mutually exclusive with `page_index`.
2812
+ Per ADR-0009 §D2.
2813
+ example: 0
2814
+
2815
+ # ============================================
2816
+ # WORKFLOW STATE CHANGED (per ticket I24)
2817
+ # ============================================
2818
+
2819
+ WorkflowStateChanged:
2820
+ type: object
2821
+ description: |
2822
+ Workflow-level state-transition event published by the API
2823
+ when a workflow's `status` changes. Per ticket
2824
+ [I24 `e50uXLcl`](https://trello.com/c/e50uXLcl) + plan v5
2825
+ round 9.
2826
+
2827
+ Emitted on every transition (e.g. `pending → in_progress`,
2828
+ `in_progress → paused_insufficient_credits`,
2829
+ `paused_insufficient_credits → in_progress` after resume,
2830
+ `in_progress → cancelled`, `paused_insufficient_credits →
2831
+ expired`, the terminal `completed` / `failed` /
2832
+ `partially_failed`). The SSE stream's
2833
+ `workflow.completed` / `workflow.failed` /
2834
+ `workflow.partially_failed` events are the frontend-facing
2835
+ subset of these transitions; this event covers the full
2836
+ lifecycle for backend consumers (audit log, dashboards,
2837
+ webhook subscribers).
2838
+
2839
+ `reason` is a free-form string carrying advisory cause —
2840
+ same opacity precedent as `OperationMetrics.re_encode_reason`
2841
+ (per I16-CONS) and `SseWorkflowTerminalData.reason` (per
2842
+ I27). `billing_effect` is present only on transitions to
2843
+ `cancelled` or `expired` — both states release any unspent
2844
+ reservation portion (see `WorkflowCancelBillingEffect` in
2845
+ `openapi/api.yaml`).
2846
+ required:
2847
+ - workflow_id
2848
+ - previous_status
2849
+ - current_status
2850
+ - changed_at
2851
+ properties:
2852
+ workflow_id:
2853
+ type: string
2854
+ format: uuid
2855
+ description: UUID-v7 workflow identifier.
2856
+ previous_status:
2857
+ type: string
2858
+ enum:
2859
+ - pending
2860
+ - in_progress
2861
+ - completed
2862
+ - failed
2863
+ - partially_failed
2864
+ - paused_insufficient_credits
2865
+ - cancelled
2866
+ - expired
2867
+ current_status:
2868
+ type: string
2869
+ enum:
2870
+ - pending
2871
+ - in_progress
2872
+ - completed
2873
+ - failed
2874
+ - partially_failed
2875
+ - paused_insufficient_credits
2876
+ - cancelled
2877
+ - expired
2878
+ changed_at:
2879
+ type: string
2880
+ format: date-time
2881
+ reason:
2882
+ type: string
2883
+ description: |
2884
+ Optional advisory reason. Free-form string; not enumerated.
2885
+ Examples: `all jobs completed successfully`,
2886
+ `next reservation exceeded available_credits` (when
2887
+ transitioning to `paused_insufficient_credits`),
2888
+ `caller requested cancel`, `paused_insufficient_credits
2889
+ past expires_at`, `dispatch timeout`.
2890
+ billing_effect:
2891
+ type: string
2892
+ enum:
2893
+ - unspent_reservation_released
2894
+ - none
2895
+ description: |
2896
+ Present on transitions to `cancelled` or `expired`.
2897
+ Mirrors `WorkflowCancelBillingEffect` from
2898
+ `openapi/api.yaml`. Absent on other transitions.
2899
+
1790
2900
  # ============================================
1791
2901
  # METRICS
1792
2902
  # ============================================
@@ -1801,7 +2911,7 @@ components:
1801
2911
  original_size_bytes:
1802
2912
  type: integer
1803
2913
  format: int64
1804
- description: Original file size in bytes (compress, convert, thumbnail, watermark)
2914
+ description: Original file size in bytes (compress, convert, thumbnail, image_watermark, text_watermark)
1805
2915
  example: 5242880
1806
2916
  output_size_bytes:
1807
2917
  type: integer
@@ -1835,6 +2945,381 @@ components:
1835
2945
  format: int64
1836
2946
  description: Sum of all input file sizes (merge, archive)
1837
2947
  example: 52428800
2948
+ re_encode_decision:
2949
+ # Named schema lifted from inline per ticket QokFw8VR.
2950
+ $ref: '#/components/schemas/ReEncodeDecision'
2951
+ re_encode_reason:
2952
+ type: string
2953
+ description: |
2954
+ Advisory explanation for `re_encode_decision` (e.g.
2955
+ `all_inputs_compatible`, `explicit_always_mode`,
2956
+ `input_codec_mismatch`, `input_framerate_mismatch`).
2957
+ Free-form string — not an enum — so the Lambda can emit
2958
+ human-readable diagnostics that evolve without contract
2959
+ churn (compatibility-probe algorithm stays opaque per
2960
+ plan v5 §F8.2). Per ticket I16-CONS.
2961
+ example: input_codec_mismatch
2962
+
2963
+ # ============================================
2964
+ # UPLOAD PROBE PAYLOADS (per ticket vBlEurU7)
2965
+ # ============================================
2966
+
2967
+ UploadProbeRequest:
2968
+ type: object
2969
+ description: |
2970
+ Request payload for the upload-probe Lambda. Shared between
2971
+ the synchronous Lambda RequestResponse path (backing
2972
+ `POST /api/uploads/{id}/probe`) and the asynchronous
2973
+ SNS->SQS->Lambda path (eager `rich_metadata` enrichment on
2974
+ upload finalize). Per ticket
2975
+ [vBlEurU7](https://trello.com/c/vBlEurU7) +
2976
+ [rdBHmTfa](https://trello.com/c/rdBHmTfa).
2977
+
2978
+ The Lambda detects which path it is on from the AWS event
2979
+ source structure — there is no flag in the payload that
2980
+ distinguishes sync vs async. The shape is intentionally
2981
+ small so SDK generators emit a single typed binding the API
2982
+ layer can construct from either invocation path.
2983
+ required:
2984
+ - file_id
2985
+ - source_bucket
2986
+ - source_key
2987
+ - file_type
2988
+ - idempotency_key
2989
+ properties:
2990
+ file_id:
2991
+ type: string
2992
+ format: uuid
2993
+ description: |
2994
+ Upload file identifier (UUID v7). Mirrors the path
2995
+ parameter on `POST /api/uploads/{id}/probe` for the sync
2996
+ path and the upload row's primary key for the async path.
2997
+ example: "019539ab-1111-7000-8000-000000000010"
2998
+ source_bucket:
2999
+ type: string
3000
+ description: |
3001
+ S3 bucket containing the uploaded file. Always the
3002
+ tenant-scoped input bucket
3003
+ (`gisl-{env}-{region}-input`).
3004
+ example: "gisl-stg-euw1-input"
3005
+ source_key:
3006
+ type: string
3007
+ description: |
3008
+ S3 key of the uploaded file under `source_bucket`.
3009
+ example: "uploads/019539ab-1111-7000-8000-000000000010/clip.mp4"
3010
+ file_type:
3011
+ type: string
3012
+ description: |
3013
+ MIME type of the uploaded file (e.g. `video/mp4`,
3014
+ `audio/mpeg`, `application/pdf`) as recorded on the
3015
+ upload row at finalize time. Field name follows the
3016
+ AsyncAPI events.yaml convention (`OperationRequest.file_type`
3017
+ and the rest of the message-payload schemas in this spec
3018
+ consistently use `file_type` for MIME, even though
3019
+ `openapi/api.yaml` uses `mime_type` for the same concept
3020
+ on the REST surface). The probe uses this as a hint for
3021
+ which extractor pipeline to run; mismatches against the
3022
+ actual container surface as `format_mismatch` in the
3023
+ completion's `error_code`.
3024
+ example: "video/mp4"
3025
+ idempotency_key:
3026
+ type: string
3027
+ minLength: 1
3028
+ maxLength: 200
3029
+ description: |
3030
+ Caller-supplied idempotency token. Echoed back on the
3031
+ `UploadProbeCompletion` (async branch) so the API
3032
+ consumer can deduplicate against the upload row.
3033
+
3034
+ Conventions:
3035
+ - Sync path: typically the `file_id` itself, since the
3036
+ probe is cached server-side per upload (see
3037
+ `UploadProbeMediaMetadata.probed_at` description in
3038
+ `openapi/api.yaml`).
3039
+ - Async path: the upload finalize event identifier
3040
+ (e.g. `upload-finalize:{file_id}`) so retries of the
3041
+ same finalize event collapse to a single completion.
3042
+ example: "upload-finalize:019539ab-1111-7000-8000-000000000010"
3043
+ requested_at:
3044
+ type: string
3045
+ format: date-time
3046
+ description: |
3047
+ ISO-8601 timestamp at which the probe request was issued.
3048
+ Optional; populated when the API publisher records it
3049
+ for log correlation. Not used by the Lambda for any
3050
+ decision.
3051
+ example: "2026-05-06T13:50:00Z"
3052
+
3053
+ UploadProbeCompletion:
3054
+ type: object
3055
+ description: |
3056
+ Completion payload published by the upload-probe Lambda to
3057
+ the `upload-probe-completions` SQS queue on the async
3058
+ branch. Mirrors `UploadProbeResponse` (`openapi/api.yaml`)
3059
+ for the success case and additionally carries failure
3060
+ fields (`error_code`, `error_message`) so the API consumer
3061
+ can persist Lambda-side failures (S3 download failed,
3062
+ decoder crashed, etc.) on the upload row alongside
3063
+ successful probes.
3064
+
3065
+ **Conditional schema (mirrors `OperationResult`):**
3066
+ - When `error_code` is **absent**, the payload is treated as
3067
+ a successful probe and `probe_status`,
3068
+ `processing_class_pre_assignment`, and `media_metadata`
3069
+ are REQUIRED.
3070
+ - When `error_code` is **present**, the payload is treated as
3071
+ a Lambda-side failure and only `error_code` +
3072
+ `error_message` are REQUIRED on top of the always-required
3073
+ identification + provenance fields. `probe_status` and
3074
+ peers MAY be absent in this case.
3075
+
3076
+ Note that `probe_status: corrupt` / `unsupported_codec` /
3077
+ `missing_metadata` are **successful** probes that report an
3078
+ unhealthy file — they do NOT populate `error_code`. Reserve
3079
+ `error_code` for "the probe pipeline itself failed".
3080
+
3081
+ Sync invocations do not produce this shape — they return
3082
+ `UploadProbeResponse` directly.
3083
+ required:
3084
+ - file_id
3085
+ - idempotency_key
3086
+ - probed_at
3087
+ - probe_version
3088
+ if:
3089
+ not:
3090
+ required:
3091
+ - error_code
3092
+ then:
3093
+ required:
3094
+ - probe_status
3095
+ - processing_class_pre_assignment
3096
+ - media_metadata
3097
+ else:
3098
+ required:
3099
+ - error_code
3100
+ - error_message
3101
+ properties:
3102
+ file_id:
3103
+ type: string
3104
+ format: uuid
3105
+ description: Upload file identifier (UUID v7) — correlation key.
3106
+ example: "019539ab-1111-7000-8000-000000000010"
3107
+ idempotency_key:
3108
+ type: string
3109
+ description: |
3110
+ Echoed from the originating `UploadProbeRequest` for
3111
+ deduplication on the API consumer side.
3112
+ example: "upload-finalize:019539ab-1111-7000-8000-000000000010"
3113
+ probe_status:
3114
+ $ref: '#/components/schemas/UploadProbeStatus'
3115
+ processing_class_pre_assignment:
3116
+ $ref: '#/components/schemas/UploadProbeProcessingClass'
3117
+ media_metadata:
3118
+ $ref: '#/components/schemas/UploadProbeMediaMetadata'
3119
+ error_code:
3120
+ type: string
3121
+ description: |
3122
+ Machine-readable error code when the probe pipeline
3123
+ itself failed. Reuses the same vocabulary as
3124
+ `OperationResult.error_code` where applicable
3125
+ (`s3_download_failed`, `s3_access_denied`, `timeout`,
3126
+ `out_of_memory`, `decode_failed`, `processing_failed`,
3127
+ `unknown`). Absent on successful probes — including
3128
+ probes that report `probe_status: corrupt /
3129
+ unsupported_codec / missing_metadata`, which are
3130
+ successful probes about an unhealthy file.
3131
+ example: "s3_download_failed"
3132
+ error_message:
3133
+ type: string
3134
+ description: |
3135
+ Human-readable error message paired with `error_code`.
3136
+ Absent on successful probes.
3137
+ example: "Failed to download source file from S3"
3138
+ probed_at:
3139
+ type: string
3140
+ format: date-time
3141
+ description: |
3142
+ ISO-8601 timestamp at which the probe ran (or attempted
3143
+ to run, for failure cases). Always present so the API
3144
+ consumer can timestamp the upload row regardless of
3145
+ outcome. For successful probes this matches
3146
+ `media_metadata.probed_at`; the duplicate top-level
3147
+ field exists so the failure case can still carry a
3148
+ timestamp even when `media_metadata` is omitted.
3149
+ example: "2026-05-06T13:50:01Z"
3150
+ probe_version:
3151
+ type: string
3152
+ description: |
3153
+ Semver of the upload-probe Lambda implementation that
3154
+ produced this completion. Useful for cache invalidation
3155
+ on the API side (re-probe when the prober ships a new
3156
+ extractor) and for log correlation.
3157
+ example: "1.0.0"
3158
+
3159
+ UploadProbeStatus:
3160
+ type: string
3161
+ description: |
3162
+ Outcome of a successful preflight probe. Mirrors
3163
+ `UploadProbeStatus` in `openapi/api.yaml` so SDK generators
3164
+ consuming both specs emit a single typed binding.
3165
+
3166
+ Cross-spec parity is verified by
3167
+ `tests/test_upload_probe_schemas.py` — the OpenAPI side is
3168
+ the source of truth for the value set.
3169
+
3170
+ Note: a `probe_status` value here means the probe pipeline
3171
+ succeeded. Lambda-side failures on the async branch are
3172
+ signalled via `UploadProbeCompletion.error_code` and do not
3173
+ require populating this field.
3174
+ enum:
3175
+ - ok
3176
+ - corrupt
3177
+ - unsupported_codec
3178
+ - missing_metadata
3179
+
3180
+ UploadProbeProcessingClass:
3181
+ type: string
3182
+ description: |
3183
+ Tier-aware pre-assignment. Mirrors
3184
+ `UploadProbeProcessingClass` in `openapi/api.yaml` so SDK
3185
+ generators consuming both specs emit a single typed binding.
3186
+
3187
+ Cross-spec parity is verified by
3188
+ `tests/test_upload_probe_schemas.py` — the OpenAPI side is
3189
+ the source of truth for the value set.
3190
+ enum:
3191
+ - short_form
3192
+ - long_form
3193
+ - blocked
3194
+
3195
+ UploadProbeMediaMetadata:
3196
+ type: object
3197
+ description: |
3198
+ Probe-extracted media metadata. Mirrors
3199
+ `UploadProbeMediaMetadata` in `openapi/api.yaml` so SDK
3200
+ generators consuming both specs emit a single typed binding
3201
+ instead of two `AnonymousSchema_N` placeholders.
3202
+
3203
+ AsyncAPI 3.0 cannot `$ref` across files into the OpenAPI
3204
+ spec, so the shape is duplicated here verbatim. Cross-spec
3205
+ parity is verified by
3206
+ `tests/test_upload_probe_schemas.py` — the OpenAPI side is
3207
+ the source of truth.
3208
+
3209
+ Schema covers the **union** of fields needed by the API's
3210
+ `MetadataResponse` projection across video / audio /
3211
+ document inputs (per the M2 Epic re-scope —
3212
+ [`SrHwuvIl`](https://trello.com/c/SrHwuvIl) — which makes
3213
+ this probe Lambda the universal non-image metadata source
3214
+ feeding `rich_metadata`). The Lambda extractor only
3215
+ populates fields it can derive from the actual container;
3216
+ unrelated fields are simply omitted (absent fields are NOT
3217
+ errors — they reflect "this metadata is not applicable /
3218
+ not extractable" rather than "this metadata failed
3219
+ validation"). `probed_at` is always present.
3220
+ required:
3221
+ - probed_at
3222
+ properties:
3223
+ duration_seconds:
3224
+ type: integer
3225
+ minimum: 0
3226
+ description: Container-reported duration (audio + video).
3227
+ width:
3228
+ type: integer
3229
+ minimum: 1
3230
+ description: Pixel width (image + video; best-effort document).
3231
+ height:
3232
+ type: integer
3233
+ minimum: 1
3234
+ description: Pixel height (image + video; best-effort document).
3235
+ codec:
3236
+ type: string
3237
+ description: |
3238
+ Primary codec identifier as reported by the prober. For
3239
+ video inputs this is the **video** codec; for audio-only
3240
+ inputs this is the audio codec. For video files with
3241
+ audio tracks, the audio codec is reported separately as
3242
+ `audio_codec`. Examples: `h264`, `h265`, `vp9`, `av1`,
3243
+ `aac`, `opus`, `mp3`. Not constrained to an enum — codec
3244
+ strings evolve with FFmpeg releases (per ADR-0007 FFmpeg
3245
+ pin) and SDKs should string-match.
3246
+ audio_codec:
3247
+ type: string
3248
+ description: |
3249
+ Audio codec for video files that carry an audio track.
3250
+ Distinct from `codec` (which is the video codec for
3251
+ video inputs). Absent for video-only / silent video
3252
+ inputs and for audio-only inputs (audio-only inputs put
3253
+ their codec under `codec`).
3254
+ container:
3255
+ type: string
3256
+ description: |
3257
+ Container format. Examples: `mp4`, `webm`, `mov`,
3258
+ `mkv`, `mp3`, `wav`, `flac`. Not constrained to an
3259
+ enum.
3260
+ fps:
3261
+ type: number
3262
+ minimum: 0
3263
+ description: |
3264
+ Container-reported frame rate (video only). Number, not
3265
+ integer — frame rates are commonly fractional (e.g.
3266
+ `29.97`, `23.976`).
3267
+ bitrate_bps:
3268
+ type: integer
3269
+ minimum: 0
3270
+ description: |
3271
+ Overall stream bitrate, **bits per second**. Units in
3272
+ the field name, mirroring `duration_seconds`, so SDKs
3273
+ avoid the kbps-vs-bps guessing trap. Container-reported
3274
+ for video + audio.
3275
+ audio_layout:
3276
+ type: string
3277
+ description: |
3278
+ Channel layout, human-display form. Examples: `mono`,
3279
+ `stereo`, `5.1`, `7.1`. Not constrained. Pairs with the
3280
+ numeric `channels` field (which is what API consumers
3281
+ project into `rich_metadata`); `audio_layout` is
3282
+ preserved on the probe row for UI display ("5.1
3283
+ surround") without forcing the UI to derive a label
3284
+ from the integer.
3285
+ channels:
3286
+ type: integer
3287
+ minimum: 1
3288
+ description: |
3289
+ Numeric audio channel count derived by the Lambda from
3290
+ ffprobe output. Examples: `1` (mono), `2` (stereo), `6`
3291
+ (5.1), `8` (7.1). Pairs with `audio_layout`. Audio +
3292
+ video.
3293
+ sample_rate_hz:
3294
+ type: integer
3295
+ minimum: 1
3296
+ description: |
3297
+ Audio sample rate, **Hertz**. Units in the field name,
3298
+ mirroring `duration_seconds` / `bitrate_bps`. Examples:
3299
+ `44100`, `48000`. Audio + video-with-audio.
3300
+ page_count:
3301
+ type: integer
3302
+ minimum: 0
3303
+ description: |
3304
+ Document page count (PDF / EPUB / DOCX / XLSX / PPTX /
3305
+ ODT / ODS / ODP). `0` for a document the prober opened
3306
+ but found empty.
3307
+ dpi:
3308
+ type: integer
3309
+ minimum: 1
3310
+ description: |
3311
+ Document resolution, single-axis (dots per inch).
3312
+ Single-axis matches the existing `MetadataResponse`
3313
+ contract on the OpenAPI side; the prober reports the
3314
+ page-1 horizontal DPI when the document declares it,
3315
+ otherwise omits the field.
3316
+ probed_at:
3317
+ type: string
3318
+ format: date-time
3319
+ description: |
3320
+ ISO-8601 timestamp at which the probe ran. Idempotent
3321
+ for the same `file_id` — subsequent probe calls return
3322
+ the cached `probed_at`.
1838
3323
 
1839
3324
  # ============================================
1840
3325
  # ENUMS
@@ -1843,52 +3328,102 @@ components:
1843
3328
  OperationType:
1844
3329
  type: string
1845
3330
  description: |
1846
- Operation type. Appears both inside the message payload and, for
1847
- non-compression operations, as the SNS `operation_type` filter
1848
- attribute on the `operations` topic. Compression operations are
1849
- routed by `job_type` on the separate `job-requests` topic — see
1850
- `JobRequestAttributes` and the top-level `info.description`.
3331
+ Operation type as carried inside the message **payload** body. This
3332
+ enum is deliberately narrower than the SNS `operation_type`
3333
+ MessageAttribute: the attribute enum permits per-media thumbnail
3334
+ sub-types (`thumbnail_image`, `thumbnail_video`,
3335
+ `thumbnail_document`, `thumbnail_office`) for routing on the
3336
+ `operations` topic, but the payload body always carries the
3337
+ semantic base value (`thumbnail`). See
3338
+ `OperationRequestAttributes.operation_type` for the broader
3339
+ attribute enum and the routing contract. Note that the attribute
3340
+ values the publisher currently emits are governed by the
3341
+ thumbnail migration-window note (top-level `info.description`
3342
+ and `OperationRequestMessage.description`).
3343
+
3344
+ Compression operations are routed by `job_type` on the separate
3345
+ `job-requests` topic — see `JobRequestAttributes` and the top-level
3346
+ `info.description`.
1851
3347
 
1852
3348
  - `compress`: Reduce file size (image, video, audio, document).
1853
3349
  Routes via `job_type` on the `job-requests` topic.
1854
- - `thumbnail`: Legacy thumbnail value. Routes via
1855
- `operation_type=thumbnail` on the `operations` topic to the
1856
- legacy `ops-thumbnail` queue. Currently the only thumbnail
1857
- value the API publisher emits.
1858
- - `thumbnail_image`: Image thumbnail sub-type. Routes via
1859
- `operation_type=thumbnail_image` to `ops-thumbnail-image`.
1860
- Not yet emitted by the API publisher — adoption is a
1861
- follow-up API PR with input-MIME dispatch.
1862
- - `thumbnail_video`: Video thumbnail sub-type. Routes via
1863
- `operation_type=thumbnail_video` to `ops-thumbnail-video`.
1864
- Not yet emitted.
1865
- - `thumbnail_document`: PDF/EPUB thumbnail sub-type. Routes via
1866
- `operation_type=thumbnail_document` to `ops-thumbnail-document`.
1867
- Not yet emitted.
1868
- - `thumbnail_office`: Office document (DOCX/XLSX/PPTX/ODT/ODS/ODP)
1869
- thumbnail sub-type. Routes via `operation_type=thumbnail_office`
1870
- to `ops-thumbnail-office`. Not yet emitted.
1871
- - `watermark`: Apply branding/protection (image only). Routes
1872
- via `operation_type=watermark` to `ops-watermark`.
3350
+ - `thumbnail`: Thumbnail generation. The payload always carries
3351
+ the base value; the SNS `operation_type` attribute enum
3352
+ additionally permits one of the four sub-type values
3353
+ (`thumbnail_image`, `thumbnail_video`, `thumbnail_document`,
3354
+ `thumbnail_office`) for routing the request to the matching
3355
+ per-media Lambda. During the migration window, the legacy
3356
+ `operation_type=thumbnail` attribute value also remains a
3357
+ valid routing target (to `ops-thumbnail`). See the
3358
+ migration-window note for which values the publisher currently
3359
+ emits.
3360
+ - `image_watermark`: Apply image overlay (file or external_source)
3361
+ onto a base media asset (Path B multi-input with role
3362
+ base+overlay per JobInputV2.role). Stable for static-image bases
3363
+ (image/jpeg, image/png, image/webp); animated GIF and video
3364
+ bases are advertised in `image_watermark.yaml` as `planned` via
3365
+ parallel mime_groups (`image_gif`, `video`) — dispatch returns
3366
+ `feature_not_available` (422) until Lambda support ships.
3367
+ Routes via `operation_type=image_watermark` to
3368
+ `ops-image-watermark`. Per ADR-0004 + I4-CONS + I5 (Trello
3369
+ AKZiOXnd).
3370
+ - `text_watermark`: Render a text overlay (Liberation Sans) onto
3371
+ an image. Single-input. Routes via
3372
+ `operation_type=text_watermark` to `ops-text-watermark`.
3373
+ Per ADR-0004 + I4-CONS.
1873
3374
  - `convert`: Change file format (image, video, audio, document).
1874
3375
  Routes via `operation_type=convert` to `ops-convert`.
1875
3376
  - `merge`: Concatenate/combine multiple files (image, video,
1876
- audio, document/PDF). Routes via `operation_type=merge` to
1877
- `ops-merge`. A single Lambda handles all merge output types.
3377
+ audio). Image inputs merge into animated GIF or slideshow
3378
+ video; image collage/grid and PDF concatenation are not
3379
+ supported by the V1 Lambda. Routes via `operation_type=merge`
3380
+ to `ops-merge`. A single Lambda handles the supported merge
3381
+ output types.
1878
3382
  - `archive`: Bundle files into ZIP/tar.gz (media-agnostic).
1879
3383
  Routes via `operation_type=archive` to `ops-archive`. No
1880
3384
  `media_group` attribute is set for archive.
3385
+ - `custom_luma`: Apply a caller-uploaded luma matte to a base
3386
+ video for a custom luma-matte transition effect. Multi-input
3387
+ (Path B with `role: base` + `role: transition_mask` per
3388
+ `JobInputV2.role`). `availability: planned` +
3389
+ `required_tier: pro` per the operation schema; the API
3390
+ rejects workflow-create with `feature_not_available` (422)
3391
+ until the Lambda ships, so traffic does not reach SNS today.
3392
+ When Lambda support lands, routes via
3393
+ `operation_type=custom_luma` to `ops-custom-luma`. Per ADR-
3394
+ 0004 + I29 (Trello EPUE5Vs1).
3395
+ - `audio_overlay`: Mix a secondary audio asset over a primary
3396
+ audio or video base (DJ tags, podcast intros/outros, station
3397
+ IDs, jingles). Multi-input (Path B with `role: base` +
3398
+ `role: overlay` per `JobInputV2.role`). Both mime_groups
3399
+ (`audio`, `video`) are `availability: planned` per the
3400
+ operation schema; the API rejects workflow-create with
3401
+ `feature_not_available` (422) until the Lambda ships, so
3402
+ traffic does not reach SNS today. **NOT** the same as
3403
+ `audio_watermark` (steganographic, owned by I20). Per ADR-
3404
+ 0004 + I19 (Trello Xr3Z4GBF).
3405
+ - `audio_watermark`: Embed a steganographic forensic watermark
3406
+ into an audio asset (or a video's audio track) — Cinavia /
3407
+ Resemble PerTh territory. Single-input. Both mime_groups
3408
+ (`audio`, `video` — embed in video's audio track) are
3409
+ `availability: planned` + `required_tier: enterprise` per
3410
+ the operation schema; the API rejects workflow-create with
3411
+ `feature_not_available` (422) + `feature_tier_restricted`
3412
+ (403) until the Lambda ships, so traffic does not reach SNS
3413
+ today. Pairs with `POST /api/audio-watermark/decode` for
3414
+ own-watermarks-only extraction. Per ADR-0004 + I20 (Trello
3415
+ omiCq7Vn).
1881
3416
  enum:
1882
3417
  - compress
1883
3418
  - thumbnail
1884
- - thumbnail_image
1885
- - thumbnail_video
1886
- - thumbnail_document
1887
- - thumbnail_office
1888
- - watermark
3419
+ - image_watermark
3420
+ - text_watermark
1889
3421
  - convert
1890
3422
  - merge
1891
3423
  - archive
3424
+ - custom_luma
3425
+ - audio_overlay
3426
+ - audio_watermark
1892
3427
 
1893
3428
  MediaGroup:
1894
3429
  type: string
@@ -1909,10 +3444,9 @@ components:
1909
3444
  ODS, ODP)
1910
3445
 
1911
3446
  For merge, derived from `output_type`:
1912
- - output_type=image or gif -> image
3447
+ - output_type=gif -> image
1913
3448
  - output_type=video -> video
1914
3449
  - output_type=audio -> audio
1915
- - output_type=document -> document
1916
3450
 
1917
3451
  Omitted from the SNS attributes for archive (media-agnostic). The
1918
3452
  four thumbnail sub-types map to media_group as follows:
@@ -1935,30 +3469,101 @@ components:
1935
3469
  regardless of the `output_type` value — `output_type` is read by
1936
3470
  the merge Lambda to decide the internal encoding path and output
1937
3471
  format, not to influence SNS routing.
1938
- - `image`: Collage/grid (ImageMagick montage inside the merge Lambda)
1939
3472
  - `gif`: Animated GIF (ImageMagick convert inside the merge Lambda)
1940
3473
  - `video`: Slideshow from images or video concatenation (FFmpeg inside the merge Lambda)
1941
3474
  - `audio`: Audio concatenation (FFmpeg inside the merge Lambda)
1942
- - `document`: PDF concatenation (qpdf inside the merge Lambda)
1943
3475
  enum:
1944
- - image
1945
3476
  - gif
1946
3477
  - video
1947
3478
  - audio
1948
- - document
3479
+
3480
+ JobInputRole:
3481
+ type: string
3482
+ description: |
3483
+ Role for role-based multi-input operations. Mirrors
3484
+ `JobInputV2.role` in `openapi/api.yaml`:
3485
+ - `image_watermark`: REQUIRED on each entry — exactly
3486
+ one `base` + one `overlay` per job (per ticket I4-CONS).
3487
+ - `custom_luma`: REQUIRED — exactly one `base` + one
3488
+ `transition_mask` per job (per ticket I29).
3489
+ - `audio_overlay`: REQUIRED — exactly one `base` + one
3490
+ `overlay` per job (per ticket I19); reuses
3491
+ `image_watermark`'s role values.
3492
+ - `merge` / `archive`: omit (all inputs share the same
3493
+ role). Future operations may widen this enum.
3494
+
3495
+ Named-schema form lifted from inline at `SourceEntry.role`
3496
+ per ticket QokFw8VR. The OpenAPI side at `JobInputV2.role`
3497
+ remains an inline enum today — symmetric extraction tracked
3498
+ as a follow-up; value-set parity is verified by inspection
3499
+ (both lists must remain identical).
3500
+ enum:
3501
+ - base
3502
+ - overlay
3503
+ - transition_mask
3504
+
3505
+ ReEncodeDecision:
3506
+ type: string
3507
+ description: |
3508
+ Path chosen for `merge.video` when `re_encode_mode=auto`.
3509
+ Server reports the actual path so callers can see why
3510
+ `auto` took the slow path. Absent for non-`merge.video`
3511
+ operations and for `merge.video` when `re_encode_mode` is
3512
+ `always` or `never` (path was caller-fixed). Per ticket
3513
+ I16-CONS (Trello 7nCZXEru). Workflow-create-time surface
3514
+ on `processing_plan.jobs[]` lands with I15-CONS
3515
+ (Trello YZpBKzOM).
3516
+
3517
+ Named-schema form lifted from inline at
3518
+ `OperationMetrics.re_encode_decision` per ticket QokFw8VR.
3519
+ enum:
3520
+ - stream_copy
3521
+ - re_encode
1949
3522
 
1950
3523
  ProgressStatus:
1951
3524
  type: string
1952
3525
  description: |
1953
- Status values for progress updates (non-terminal):
1954
- - started: Lambda picked up the operation
1955
- - downloading: Downloading source file(s) from S3
1956
- - processing: Actively processing (compressing, merging, etc.)
1957
- - uploading: Uploading result to S3
3526
+ Status values for progress updates (non-terminal). Backwards-
3527
+ compatibly extended for long-form video operations per ticket
3528
+ [I27 `1R3K3bsG`](https://trello.com/c/1R3K3bsG) + plan v5
3529
+ §F8.3 round 10 the `probing` / `decoding` / `encoding`
3530
+ statuses give callers visibility into long-form jobs that
3531
+ internally process inputs sequentially without forcing
3532
+ callers to decompose into multiple jobs.
3533
+
3534
+ Non-long-form operations continue to emit only `started` /
3535
+ `downloading` / `processing` / `uploading` (the V1 pattern);
3536
+ clients that don't need phase visibility can ignore the new
3537
+ statuses without behavioural change.
3538
+
3539
+ - `started`: Lambda picked up the operation.
3540
+ - `downloading`: Downloading source file(s) from S3.
3541
+ - `probing`: Long-form video — server-side metadata probe
3542
+ on inputs (codec / container / framerate / resolution
3543
+ / pixel-format detection driving `re_encode_decision`).
3544
+ Pairs with `phase_input_index` / `phase_total_inputs` so
3545
+ the frontend can render "probing input 2/4". Per I15-CONS
3546
+ / I16-CONS.
3547
+ - `decoding`: Long-form video — decoding inputs ahead of
3548
+ the encode pass. Pairs with `phase_*` fields.
3549
+ - `processing`: Actively processing (compressing, merging,
3550
+ watermarking, etc.). The catch-all phase used by
3551
+ short-form operations and short-form-equivalent stages of
3552
+ long-form operations. May pair with `phase_*` fields when
3553
+ the operation is long-form.
3554
+ - `encoding`: Long-form video — explicit encode-pass status
3555
+ (vs the umbrella `processing`). Surfaces "encoding output
3556
+ 42%" when the encode pass is long enough to deserve
3557
+ dedicated visibility. May pair with `phase_*` fields for
3558
+ multi-output operations.
3559
+ - `uploading`: Uploading result to S3.
1958
3560
  enum:
1959
3561
  - started
1960
3562
  - downloading
3563
+ - probing
3564
+ - decoding
1961
3565
  - processing
3566
+ - encoding
1962
3567
  - uploading
1963
3568
 
1964
3569
  ResultStatus: