@giveitsmaller/contracts 0.8.0 → 0.16.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.
- package/asyncapi/events.yaml +806 -173
- package/availability/availability.json +355 -98
- package/dist/asyncapi/Failure.d.ts +2 -0
- package/dist/asyncapi/LongFormJobMessage.d.ts +4 -3
- package/dist/asyncapi/LongFormOperationType.d.ts +5 -0
- package/dist/asyncapi/LongFormOperationType.js +6 -0
- package/dist/asyncapi/LongFormProcessingClass.d.ts +4 -0
- package/dist/asyncapi/LongFormProcessingClass.js +5 -0
- package/dist/asyncapi/MultiOutputCompletion.d.ts +2 -0
- package/dist/asyncapi/{NotificationsOperationsQueue.d.ts → NotificationsJobsQueue.d.ts} +2 -2
- package/dist/asyncapi/OperationResultMetadata.d.ts +4 -0
- package/dist/asyncapi/OperationResultMetadata.js +1 -0
- package/dist/asyncapi/OperationType.d.ts +2 -1
- package/dist/asyncapi/OperationType.js +1 -0
- package/dist/asyncapi/PageIndexed.d.ts +1 -0
- package/dist/asyncapi/PositionIndexed.d.ts +1 -0
- package/dist/asyncapi/SingleOutputCompletion.d.ts +2 -0
- package/dist/asyncapi/SourceEntry.d.ts +4 -0
- package/dist/asyncapi/Unindexed.d.ts +1 -0
- package/dist/asyncapi/index.d.ts +4 -2
- package/dist/asyncapi/index.js +2 -1
- package/dist/openapi/models/AudioWatermarkDecodeRequest.d.ts +2 -2
- package/dist/openapi/models/AudioWatermarkDecodeRequest.js +2 -2
- package/dist/openapi/models/AudioWatermarkDecodeResponse.d.ts +2 -2
- package/dist/openapi/models/AudioWatermarkDecodeResponse.js +2 -2
- package/dist/openapi/models/AuthErrorResponse.d.ts +13 -2
- package/dist/openapi/models/AuthErrorResponse.js +2 -2
- package/dist/openapi/models/AuthErrorType.d.ts +2 -2
- package/dist/openapi/models/AuthErrorType.js +2 -2
- package/dist/openapi/models/AuthRejectionEnvelope.d.ts +126 -0
- package/dist/openapi/models/AuthRejectionEnvelope.js +72 -0
- package/dist/openapi/models/AvailabilityValue.d.ts +2 -2
- package/dist/openapi/models/AvailabilityValue.js +2 -2
- package/dist/openapi/models/BalanceExhaustedResponse.d.ts +13 -2
- package/dist/openapi/models/BalanceExhaustedResponse.js +2 -2
- package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.d.ts +2 -2
- package/dist/openapi/models/BalanceExhaustedResponseAllOfLinks.js +2 -2
- package/dist/openapi/models/CallbackEventType.d.ts +2 -2
- package/dist/openapi/models/CallbackEventType.js +2 -2
- package/dist/openapi/models/ChangePasswordRequest.d.ts +38 -0
- package/dist/openapi/models/ChangePasswordRequest.js +47 -0
- package/dist/openapi/models/CompositionPlan.d.ts +72 -0
- package/dist/openapi/models/CompositionPlan.js +53 -0
- package/dist/openapi/models/CompositionPlanJob.d.ts +39 -0
- package/dist/openapi/models/CompositionPlanJob.js +48 -0
- package/dist/openapi/models/CompositionPlanOperation.d.ts +116 -0
- package/dist/openapi/models/CompositionPlanOperation.js +62 -0
- package/dist/openapi/models/ConfirmEmailChange200Response.d.ts +46 -0
- package/dist/openapi/models/ConfirmEmailChange200Response.js +54 -0
- package/dist/openapi/models/ConfirmEmailChange200ResponseData.d.ts +32 -0
- package/dist/openapi/models/ConfirmEmailChange200ResponseData.js +43 -0
- package/dist/openapi/models/ConfirmEmailChangeRequest.d.ts +32 -0
- package/dist/openapi/models/ConfirmEmailChangeRequest.js +43 -0
- package/dist/openapi/models/ConnectionSource.d.ts +2 -2
- package/dist/openapi/models/ConnectionSource.js +2 -2
- package/dist/openapi/models/ContactRequest.d.ts +2 -2
- package/dist/openapi/models/ContactRequest.js +2 -2
- package/dist/openapi/models/ContactSubject.d.ts +2 -2
- package/dist/openapi/models/ContactSubject.js +2 -2
- package/dist/openapi/models/ContactValidationErrorResponse.d.ts +2 -2
- package/dist/openapi/models/ContactValidationErrorResponse.js +2 -2
- package/dist/openapi/models/CreateApiKey201Response.d.ts +46 -0
- package/dist/openapi/models/CreateApiKey201Response.js +54 -0
- package/dist/openapi/models/CreateApiKey201ResponseData.d.ts +56 -0
- package/dist/openapi/models/CreateApiKey201ResponseData.js +59 -0
- package/dist/openapi/models/CreateApiKeyRequest.d.ts +32 -0
- package/dist/openapi/models/CreateApiKeyRequest.js +43 -0
- package/dist/openapi/models/CreateExternalImport403Response.d.ts +2 -2
- package/dist/openapi/models/CreateExternalImport403Response.js +2 -2
- package/dist/openapi/models/CreateExternalImport422Response.d.ts +7 -3
- package/dist/openapi/models/CreateExternalImport422Response.js +18 -22
- package/dist/openapi/models/CreateWorkflow422Response.d.ts +11 -3
- package/dist/openapi/models/CreateWorkflow422Response.js +28 -36
- package/dist/openapi/models/CreditTransaction.d.ts +2 -2
- package/dist/openapi/models/CreditTransaction.js +2 -2
- package/dist/openapi/models/CreditTransactionSourceBucket.d.ts +2 -2
- package/dist/openapi/models/CreditTransactionSourceBucket.js +2 -2
- package/dist/openapi/models/CreditsBalanceResponse.d.ts +2 -2
- package/dist/openapi/models/CreditsBalanceResponse.js +2 -2
- package/dist/openapi/models/CreditsBalanceSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/CreditsBalanceSuccessEnvelope.js +2 -2
- package/dist/openapi/models/CreditsUsageResponse.d.ts +2 -2
- package/dist/openapi/models/CreditsUsageResponse.js +2 -2
- package/dist/openapi/models/CreditsUsageSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/CreditsUsageSuccessEnvelope.js +2 -2
- package/dist/openapi/models/Delivery.d.ts +2 -2
- package/dist/openapi/models/Delivery.js +2 -2
- package/dist/openapi/models/DeliveryOutputRef.d.ts +9 -2
- package/dist/openapi/models/DeliveryOutputRef.js +2 -2
- package/dist/openapi/models/DeliveryPlan.d.ts +2 -2
- package/dist/openapi/models/DeliveryPlan.js +2 -2
- package/dist/openapi/models/DeliveryPlanOutput.d.ts +17 -2
- package/dist/openapi/models/DeliveryPlanOutput.js +4 -2
- package/dist/openapi/models/DeliveryPlanReason.d.ts +2 -2
- package/dist/openapi/models/DeliveryPlanReason.js +2 -2
- package/dist/openapi/models/DeliverySelection.d.ts +6 -4
- package/dist/openapi/models/DeliverySelection.js +2 -2
- package/dist/openapi/models/EmptySuccessEnvelope.d.ts +58 -0
- package/dist/openapi/models/EmptySuccessEnvelope.js +53 -0
- package/dist/openapi/models/EndpointProjection.d.ts +12 -3
- package/dist/openapi/models/EndpointProjection.js +2 -2
- package/dist/openapi/models/ErrorEnvelope.d.ts +13 -2
- package/dist/openapi/models/ErrorEnvelope.js +2 -2
- package/dist/openapi/models/EstimateQuality.d.ts +2 -2
- package/dist/openapi/models/EstimateQuality.js +2 -2
- package/dist/openapi/models/EstimateRange.d.ts +2 -2
- package/dist/openapi/models/EstimateRange.js +2 -2
- package/dist/openapi/models/ExternalDestination.d.ts +2 -2
- package/dist/openapi/models/ExternalDestination.js +2 -2
- package/dist/openapi/models/ExternalImportCreatedResponse.d.ts +2 -2
- package/dist/openapi/models/ExternalImportCreatedResponse.js +2 -2
- package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/ExternalImportCreatedSuccessEnvelope.js +2 -2
- package/dist/openapi/models/ExternalImportRequest.d.ts +2 -2
- package/dist/openapi/models/ExternalImportRequest.js +2 -2
- package/dist/openapi/models/ExternalImportToken.d.ts +2 -2
- package/dist/openapi/models/ExternalImportToken.js +2 -2
- package/dist/openapi/models/ExternalSource.d.ts +2 -2
- package/dist/openapi/models/ExternalSource.js +2 -2
- package/dist/openapi/models/FeatureNotAvailableResponse.d.ts +19 -9
- package/dist/openapi/models/FeatureNotAvailableResponse.js +2 -2
- package/dist/openapi/models/FeatureTierRestrictedResponse.d.ts +13 -2
- package/dist/openapi/models/FeatureTierRestrictedResponse.js +2 -2
- package/dist/openapi/models/FeatureViolation.d.ts +2 -2
- package/dist/openapi/models/FeatureViolation.js +2 -2
- package/dist/openapi/models/ForgotPasswordRequest.d.ts +32 -0
- package/dist/openapi/models/ForgotPasswordRequest.js +43 -0
- package/dist/openapi/models/ImageEncodeCapabilities.d.ts +65 -0
- package/dist/openapi/models/ImageEncodeCapabilities.js +55 -0
- package/dist/openapi/models/JobDefinition.d.ts +27 -8
- package/dist/openapi/models/JobDefinition.js +2 -2
- package/dist/openapi/models/JobDownload.d.ts +2 -2
- package/dist/openapi/models/JobDownload.js +2 -2
- package/dist/openapi/models/JobInputV2.d.ts +13 -9
- package/dist/openapi/models/JobInputV2.js +5 -5
- package/dist/openapi/models/JobMediaClass.d.ts +34 -0
- package/dist/openapi/models/JobMediaClass.js +52 -0
- package/dist/openapi/models/JobOutputSource.d.ts +2 -2
- package/dist/openapi/models/JobOutputSource.js +2 -2
- package/dist/openapi/models/JobResponse.d.ts +24 -2
- package/dist/openapi/models/JobResponse.js +5 -2
- package/dist/openapi/models/JobStatus.d.ts +2 -2
- package/dist/openapi/models/JobStatus.js +2 -2
- package/dist/openapi/models/JobType.d.ts +2 -2
- package/dist/openapi/models/JobType.js +2 -2
- package/dist/openapi/models/LivenessResponse.d.ts +2 -2
- package/dist/openapi/models/LivenessResponse.js +2 -2
- package/dist/openapi/models/LoginUser200Response.d.ts +2 -2
- package/dist/openapi/models/LoginUser200Response.js +2 -2
- package/dist/openapi/models/LoginUser200ResponseData.d.ts +2 -2
- package/dist/openapi/models/LoginUser200ResponseData.js +2 -2
- package/dist/openapi/models/LoginUser200ResponseDataUser.d.ts +2 -2
- package/dist/openapi/models/LoginUser200ResponseDataUser.js +2 -2
- package/dist/openapi/models/LoginUserRequest.d.ts +2 -2
- package/dist/openapi/models/LoginUserRequest.js +2 -2
- package/dist/openapi/models/MetadataResponse.d.ts +2 -2
- package/dist/openapi/models/MetadataResponse.js +2 -2
- package/dist/openapi/models/MetadataResponseDimensions.d.ts +2 -2
- package/dist/openapi/models/MetadataResponseDimensions.js +2 -2
- package/dist/openapi/models/MetadataResponseExif.d.ts +2 -2
- package/dist/openapi/models/MetadataResponseExif.js +2 -2
- package/dist/openapi/models/MetadataResponseExifGps.d.ts +2 -2
- package/dist/openapi/models/MetadataResponseExifGps.js +2 -2
- package/dist/openapi/models/MetadataSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MetadataSuccessEnvelope.js +2 -2
- package/dist/openapi/models/MimeGroupSchema.d.ts +37 -2
- package/dist/openapi/models/MimeGroupSchema.js +5 -2
- package/dist/openapi/models/MultiInputSource.d.ts +41 -0
- package/dist/openapi/models/MultiInputSource.js +52 -0
- package/dist/openapi/models/MultipartCompleteRequest.d.ts +2 -2
- package/dist/openapi/models/MultipartCompleteRequest.js +2 -2
- package/dist/openapi/models/MultipartCompleteRequestPartsInner.d.ts +2 -2
- package/dist/openapi/models/MultipartCompleteRequestPartsInner.js +2 -2
- package/dist/openapi/models/MultipartCompleteResponse.d.ts +2 -2
- package/dist/openapi/models/MultipartCompleteResponse.js +2 -2
- package/dist/openapi/models/MultipartCompleteSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MultipartCompleteSuccessEnvelope.js +2 -2
- package/dist/openapi/models/MultipartInitiateRequestMetadataHint.d.ts +2 -2
- package/dist/openapi/models/MultipartInitiateRequestMetadataHint.js +2 -2
- package/dist/openapi/models/MultipartInitiateResponse.d.ts +2 -2
- package/dist/openapi/models/MultipartInitiateResponse.js +2 -2
- package/dist/openapi/models/MultipartInitiateSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MultipartInitiateSuccessEnvelope.js +2 -2
- package/dist/openapi/models/MultipartKeepaliveResponse.d.ts +2 -2
- package/dist/openapi/models/MultipartKeepaliveResponse.js +2 -2
- package/dist/openapi/models/MultipartKeepaliveSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MultipartKeepaliveSuccessEnvelope.js +2 -2
- package/dist/openapi/models/MultipartPartListing.d.ts +2 -2
- package/dist/openapi/models/MultipartPartListing.js +2 -2
- package/dist/openapi/models/MultipartPresignRequest.d.ts +2 -2
- package/dist/openapi/models/MultipartPresignRequest.js +2 -2
- package/dist/openapi/models/MultipartPresignResponse.d.ts +2 -2
- package/dist/openapi/models/MultipartPresignResponse.js +2 -2
- package/dist/openapi/models/MultipartPresignSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MultipartPresignSuccessEnvelope.js +2 -2
- package/dist/openapi/models/MultipartStatusResponse.d.ts +2 -2
- package/dist/openapi/models/MultipartStatusResponse.js +2 -2
- package/dist/openapi/models/MultipartStatusSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/MultipartStatusSuccessEnvelope.js +2 -2
- package/dist/openapi/models/OperationDefinition.d.ts +27 -2
- package/dist/openapi/models/OperationDefinition.js +11 -2
- package/dist/openapi/models/OperationDownload.d.ts +30 -2
- package/dist/openapi/models/OperationDownload.js +6 -2
- package/dist/openapi/models/OperationInputModel.d.ts +3 -3
- package/dist/openapi/models/OperationInputModel.js +3 -3
- package/dist/openapi/models/OperationResponse.d.ts +18 -2
- package/dist/openapi/models/OperationResponse.js +5 -2
- package/dist/openapi/models/OperationResult.d.ts +2 -2
- package/dist/openapi/models/OperationResult.js +2 -2
- package/dist/openapi/models/OperationResultMetadata.d.ts +48 -0
- package/dist/openapi/models/OperationResultMetadata.js +41 -0
- package/dist/openapi/models/OperationResultMetrics.d.ts +16 -3
- package/dist/openapi/models/OperationResultMetrics.js +2 -2
- package/dist/openapi/models/OperationSchemaDefinition.d.ts +6 -5
- package/dist/openapi/models/OperationSchemaDefinition.js +2 -2
- package/dist/openapi/models/OperationStatus.d.ts +2 -2
- package/dist/openapi/models/OperationStatus.js +2 -2
- package/dist/openapi/models/OperationType.d.ts +19 -14
- package/dist/openapi/models/OperationType.js +20 -15
- package/dist/openapi/models/OperationsSchemaResponse.d.ts +39 -4
- package/dist/openapi/models/OperationsSchemaResponse.js +8 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeatures.d.ts +55 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeatures.js +42 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDelivery.d.ts +40 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDelivery.js +45 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliveryMode.d.ts +45 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliveryMode.js +43 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliverySelection.d.ts +33 -0
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliverySelection.js +42 -0
- package/dist/openapi/models/OptionSchema.d.ts +2 -2
- package/dist/openapi/models/OptionSchema.js +2 -2
- package/dist/openapi/models/PerRoleCardinalityEntry.d.ts +2 -2
- package/dist/openapi/models/PerRoleCardinalityEntry.js +2 -2
- package/dist/openapi/models/PerValueAvailabilityEntry.d.ts +2 -2
- package/dist/openapi/models/PerValueAvailabilityEntry.js +2 -2
- package/dist/openapi/models/PresignedUrlPart.d.ts +2 -2
- package/dist/openapi/models/PresignedUrlPart.js +2 -2
- package/dist/openapi/models/ProbePendingResponse.d.ts +20 -8
- package/dist/openapi/models/ProbePendingResponse.js +2 -2
- package/dist/openapi/models/ProcessingClass.d.ts +2 -2
- package/dist/openapi/models/ProcessingClass.js +2 -2
- package/dist/openapi/models/ProcessingClassBandViolation.d.ts +2 -2
- package/dist/openapi/models/ProcessingClassBandViolation.js +2 -2
- package/dist/openapi/models/ProcessingClassConstraints.d.ts +69 -0
- package/dist/openapi/models/ProcessingClassConstraints.js +49 -0
- package/dist/openapi/models/ProcessingClassEntry.d.ts +93 -0
- package/dist/openapi/models/ProcessingClassEntry.js +57 -0
- package/dist/openapi/models/ProcessingClassExceedsBandResponse.d.ts +19 -6
- package/dist/openapi/models/ProcessingClassExceedsBandResponse.js +2 -2
- package/dist/openapi/models/ProcessingClassHint.d.ts +2 -2
- package/dist/openapi/models/ProcessingClassHint.js +2 -2
- package/dist/openapi/models/ProcessingClassReason.d.ts +2 -2
- package/dist/openapi/models/ProcessingClassReason.js +2 -2
- package/dist/openapi/models/ProcessingClassRejectReason.d.ts +2 -2
- package/dist/openapi/models/ProcessingClassRejectReason.js +2 -2
- package/dist/openapi/models/ProcessingPlan.d.ts +2 -2
- package/dist/openapi/models/ProcessingPlan.js +2 -2
- package/dist/openapi/models/ProcessingPlanJob.d.ts +2 -2
- package/dist/openapi/models/ProcessingPlanJob.js +2 -2
- package/dist/openapi/models/ReEncodeDecision.d.ts +2 -2
- package/dist/openapi/models/ReEncodeDecision.js +2 -2
- package/dist/openapi/models/ReadinessResponse.d.ts +2 -2
- package/dist/openapi/models/ReadinessResponse.js +2 -2
- package/dist/openapi/models/RegisterUser422Response.d.ts +27 -0
- package/dist/openapi/models/RegisterUser422Response.js +47 -0
- package/dist/openapi/models/RegisterUserRequest.d.ts +38 -0
- package/dist/openapi/models/RegisterUserRequest.js +47 -0
- package/dist/openapi/models/ResetPasswordRequest.d.ts +38 -0
- package/dist/openapi/models/ResetPasswordRequest.js +47 -0
- package/dist/openapi/models/ResponseEnvelope.d.ts +2 -2
- package/dist/openapi/models/ResponseEnvelope.js +2 -2
- package/dist/openapi/models/RetryResponse.d.ts +2 -2
- package/dist/openapi/models/RetryResponse.js +2 -2
- package/dist/openapi/models/RetrySuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/RetrySuccessEnvelope.js +2 -2
- package/dist/openapi/models/SseCompletionBase.d.ts +2 -2
- package/dist/openapi/models/SseCompletionBase.js +2 -2
- package/dist/openapi/models/SseEventType.d.ts +2 -2
- package/dist/openapi/models/SseEventType.js +2 -2
- package/dist/openapi/models/SseJobCompletedData.d.ts +2 -2
- package/dist/openapi/models/SseJobCompletedData.js +2 -2
- package/dist/openapi/models/SseJobFailedData.d.ts +2 -2
- package/dist/openapi/models/SseJobFailedData.js +2 -2
- package/dist/openapi/models/SseMultiOutputCompletion.d.ts +2 -2
- package/dist/openapi/models/SseMultiOutputCompletion.js +2 -2
- package/dist/openapi/models/SseMultiOutputCompletionMetrics.d.ts +2 -2
- package/dist/openapi/models/SseMultiOutputCompletionMetrics.js +2 -2
- package/dist/openapi/models/SseMultiOutputCompletionWithKind.d.ts +2 -2
- package/dist/openapi/models/SseMultiOutputCompletionWithKind.js +2 -2
- package/dist/openapi/models/SseMultiOutputResultEntry.d.ts +2 -2
- package/dist/openapi/models/SseMultiOutputResultEntry.js +2 -2
- package/dist/openapi/models/SseOperationCompletedData.d.ts +17 -3
- package/dist/openapi/models/SseOperationCompletedData.js +2 -2
- package/dist/openapi/models/SseOperationCompletionResult.d.ts +2 -2
- package/dist/openapi/models/SseOperationCompletionResult.js +2 -2
- package/dist/openapi/models/SseOperationFailedData.d.ts +2 -2
- package/dist/openapi/models/SseOperationFailedData.js +2 -2
- package/dist/openapi/models/SseOperationProgressData.d.ts +4 -3
- package/dist/openapi/models/SseOperationProgressData.js +2 -2
- package/dist/openapi/models/SseSingleOutputCompletion.d.ts +2 -2
- package/dist/openapi/models/SseSingleOutputCompletion.js +2 -2
- package/dist/openapi/models/SseWorkflowTerminalData.d.ts +2 -2
- package/dist/openapi/models/SseWorkflowTerminalData.js +2 -2
- package/dist/openapi/models/TierRestrictionKind.d.ts +2 -2
- package/dist/openapi/models/TierRestrictionKind.js +2 -2
- package/dist/openapi/models/TierRestrictionResponse.d.ts +13 -2
- package/dist/openapi/models/TierRestrictionResponse.js +2 -2
- package/dist/openapi/models/UpdateProfile200Response.d.ts +46 -0
- package/dist/openapi/models/UpdateProfile200Response.js +54 -0
- package/dist/openapi/models/UpdateProfile200ResponseData.d.ts +71 -0
- package/dist/openapi/models/UpdateProfile200ResponseData.js +67 -0
- package/dist/openapi/models/UpdateProfile422Response.d.ts +27 -0
- package/dist/openapi/models/UpdateProfile422Response.js +47 -0
- package/dist/openapi/models/UpdateProfileRequest.d.ts +44 -0
- package/dist/openapi/models/UpdateProfileRequest.js +47 -0
- package/dist/openapi/models/UploadConstraintsApplied.d.ts +2 -2
- package/dist/openapi/models/UploadConstraintsApplied.js +2 -2
- package/dist/openapi/models/UploadDurationExceedsTierResponse.d.ts +13 -2
- package/dist/openapi/models/UploadDurationExceedsTierResponse.js +2 -2
- package/dist/openapi/models/UploadFile403Response.d.ts +2 -2
- package/dist/openapi/models/UploadFile403Response.js +2 -2
- package/dist/openapi/models/UploadFile422Response.d.ts +2 -2
- package/dist/openapi/models/UploadFile422Response.js +2 -2
- package/dist/openapi/models/UploadProbeMediaMetadata.d.ts +2 -2
- package/dist/openapi/models/UploadProbeMediaMetadata.js +2 -2
- package/dist/openapi/models/UploadProbeProcessingClass.d.ts +2 -2
- package/dist/openapi/models/UploadProbeProcessingClass.js +2 -2
- package/dist/openapi/models/UploadProbeResponse.d.ts +2 -2
- package/dist/openapi/models/UploadProbeResponse.js +2 -2
- package/dist/openapi/models/UploadProbeStatus.d.ts +2 -2
- package/dist/openapi/models/UploadProbeStatus.js +2 -2
- package/dist/openapi/models/UploadProbeSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/UploadProbeSuccessEnvelope.js +2 -2
- package/dist/openapi/models/UploadResponse.d.ts +2 -2
- package/dist/openapi/models/UploadResponse.js +2 -2
- package/dist/openapi/models/UploadSizeExceedsTierResponse.d.ts +13 -2
- package/dist/openapi/models/UploadSizeExceedsTierResponse.js +2 -2
- package/dist/openapi/models/UploadSource.d.ts +2 -2
- package/dist/openapi/models/UploadSource.js +2 -2
- package/dist/openapi/models/UploadSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/UploadSuccessEnvelope.js +2 -2
- package/dist/openapi/models/UploadThresholds.d.ts +2 -2
- package/dist/openapi/models/UploadThresholds.js +2 -2
- package/dist/openapi/models/UserTier.d.ts +2 -2
- package/dist/openapi/models/UserTier.js +2 -2
- package/dist/openapi/models/ValidationErrorEnvelope.d.ts +32 -5
- package/dist/openapi/models/ValidationErrorEnvelope.js +12 -2
- package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.d.ts +2 -2
- package/dist/openapi/models/ValidationErrorEnvelopeDetailsInner.js +2 -2
- package/dist/openapi/models/VerifyEmailRequest.d.ts +32 -0
- package/dist/openapi/models/VerifyEmailRequest.js +43 -0
- package/dist/openapi/models/WarningType.d.ts +2 -2
- package/dist/openapi/models/WarningType.js +2 -2
- package/dist/openapi/models/WebhookOperationContext.d.ts +2 -2
- package/dist/openapi/models/WebhookOperationContext.js +2 -2
- package/dist/openapi/models/WebhookPayload.d.ts +2 -2
- package/dist/openapi/models/WebhookPayload.js +2 -2
- package/dist/openapi/models/WorkflowCancelBillingEffect.d.ts +2 -2
- package/dist/openapi/models/WorkflowCancelBillingEffect.js +2 -2
- package/dist/openapi/models/WorkflowCancelResponse.d.ts +2 -2
- package/dist/openapi/models/WorkflowCancelResponse.js +2 -2
- package/dist/openapi/models/WorkflowCancelSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/WorkflowCancelSuccessEnvelope.js +2 -2
- package/dist/openapi/models/WorkflowCreateRequest.d.ts +64 -4
- package/dist/openapi/models/WorkflowCreateRequest.js +10 -6
- package/dist/openapi/models/WorkflowCreateResponse.d.ts +24 -2
- package/dist/openapi/models/WorkflowCreateResponse.js +5 -2
- package/dist/openapi/models/WorkflowCreateSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/WorkflowCreateSuccessEnvelope.js +2 -2
- package/dist/openapi/models/WorkflowDownloadResponse.d.ts +2 -2
- package/dist/openapi/models/WorkflowDownloadResponse.js +2 -2
- package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/WorkflowDownloadSuccessEnvelope.js +2 -2
- package/dist/openapi/models/WorkflowEdge.d.ts +2 -2
- package/dist/openapi/models/WorkflowEdge.js +2 -2
- package/dist/openapi/models/WorkflowExpiredResponse.d.ts +13 -2
- package/dist/openapi/models/WorkflowExpiredResponse.js +2 -2
- package/dist/openapi/models/WorkflowListResponse.d.ts +50 -0
- package/dist/openapi/models/WorkflowListResponse.js +50 -0
- package/dist/openapi/models/WorkflowListSuccessEnvelope.d.ts +46 -0
- package/dist/openapi/models/WorkflowListSuccessEnvelope.js +54 -0
- package/dist/openapi/models/WorkflowPauseRequiredAction.d.ts +2 -2
- package/dist/openapi/models/WorkflowPauseRequiredAction.js +2 -2
- package/dist/openapi/models/WorkflowPausedDetail.d.ts +2 -2
- package/dist/openapi/models/WorkflowPausedDetail.js +2 -2
- package/dist/openapi/models/WorkflowPausedDetailLinks.d.ts +2 -2
- package/dist/openapi/models/WorkflowPausedDetailLinks.js +2 -2
- package/dist/openapi/models/WorkflowProcessing.d.ts +2 -2
- package/dist/openapi/models/WorkflowProcessing.js +2 -2
- package/dist/openapi/models/WorkflowResumeResponse.d.ts +2 -2
- package/dist/openapi/models/WorkflowResumeResponse.js +2 -2
- package/dist/openapi/models/WorkflowResumeSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/WorkflowResumeSuccessEnvelope.js +2 -2
- package/dist/openapi/models/WorkflowSource.d.ts +7 -4
- package/dist/openapi/models/WorkflowSource.js +2 -2
- package/dist/openapi/models/WorkflowStatus.d.ts +2 -2
- package/dist/openapi/models/WorkflowStatus.js +2 -2
- package/dist/openapi/models/WorkflowStatusResponse.d.ts +21 -2
- package/dist/openapi/models/WorkflowStatusResponse.js +5 -2
- package/dist/openapi/models/WorkflowStatusSuccessEnvelope.d.ts +2 -2
- package/dist/openapi/models/WorkflowStatusSuccessEnvelope.js +2 -2
- package/dist/openapi/models/WorkflowSummary.d.ts +60 -0
- package/dist/openapi/models/WorkflowSummary.js +57 -0
- package/dist/openapi/models/WorkflowSummaryJob.d.ts +57 -0
- package/dist/openapi/models/WorkflowSummaryJob.js +57 -0
- package/dist/openapi/models/WorkflowWarning.d.ts +2 -2
- package/dist/openapi/models/WorkflowWarning.js +2 -2
- package/dist/openapi/models/WorkflowWarningSeverity.d.ts +2 -2
- package/dist/openapi/models/WorkflowWarningSeverity.js +2 -2
- package/dist/openapi/models/index.d.ts +35 -1
- package/dist/openapi/models/index.js +35 -1
- package/dist/openapi/runtime.d.ts +2 -2
- package/dist/openapi/runtime.js +2 -2
- package/dist/operations/audio_to_video.metadata.js +1 -1
- package/dist/operations/compress.d.ts +0 -9
- package/dist/operations/compress.js +0 -6
- package/dist/operations/compress.metadata.js +4 -12
- package/dist/operations/index.d.ts +3 -0
- package/dist/operations/index.js +3 -0
- package/dist/operations/merge.d.ts +4 -0
- package/dist/operations/merge.metadata.js +12 -3
- package/dist/operations/passthrough.metadata.d.ts +2 -0
- package/dist/operations/passthrough.metadata.js +6 -0
- package/dist/operations/render_variants.d.ts +24 -0
- package/dist/operations/render_variants.js +14 -0
- package/dist/operations/render_variants.metadata.d.ts +2 -0
- package/dist/operations/render_variants.metadata.js +18 -0
- package/dist/operations/split.d.ts +8 -1
- package/dist/operations/split.js +5 -0
- package/dist/operations/split.metadata.js +22 -5
- package/dist/operations/video_watermark.metadata.js +2 -2
- package/openapi/api.yaml +2566 -288
- package/operations/schemas/archive.yaml +1 -1
- package/operations/schemas/audio_overlay.yaml +29 -13
- package/operations/schemas/audio_to_video.yaml +12 -9
- package/operations/schemas/audio_watermark.yaml +18 -16
- package/operations/schemas/compress.yaml +34 -32
- package/operations/schemas/custom_luma.yaml +21 -7
- package/operations/schemas/image_watermark.yaml +22 -7
- package/operations/schemas/merge.yaml +94 -41
- package/operations/schemas/passthrough.yaml +49 -0
- package/operations/schemas/render_variants.yaml +117 -0
- package/operations/schemas/split.yaml +76 -15
- package/operations/schemas/text_watermark.yaml +6 -6
- package/operations/schemas/thumbnail.yaml +1 -1
- package/operations/schemas/video_text_watermark.yaml +2 -2
- package/operations/schemas/video_watermark.yaml +20 -9
- package/package.json +3 -1
- package/dist/asyncapi/AnonymousSchema_253.d.ts +0 -5
- package/dist/asyncapi/AnonymousSchema_253.js +0 -6
- package/dist/openapi/models/LogoutUser200Response.d.ts +0 -50
- package/dist/openapi/models/LogoutUser200Response.js +0 -53
- /package/dist/asyncapi/{NotificationsOperationsQueue.js → NotificationsJobsQueue.js} +0 -0
package/openapi/api.yaml
CHANGED
|
@@ -14,7 +14,7 @@ info:
|
|
|
14
14
|
All mutation and query endpoints return `{ success: true, data: {...} }` on success
|
|
15
15
|
and `{ success: false, error: "...", details: [...] }` on failure.
|
|
16
16
|
Exceptions: `GET /api/operations/schema` returns raw JSON (per-tier
|
|
17
|
-
private caching with ETag
|
|
17
|
+
private caching with ETag revalidation per ADR-0002 + I3),
|
|
18
18
|
health probes return flat objects, and `POST /api/contact` returns
|
|
19
19
|
204 with no body.
|
|
20
20
|
|
|
@@ -89,7 +89,7 @@ info:
|
|
|
89
89
|
of truth instead of hardcoding magic numbers. A runtime
|
|
90
90
|
`GET /api/uploads/limits` endpoint for dynamic discovery
|
|
91
91
|
(per-tier / per-environment overrides) is a deferred follow-up.
|
|
92
|
-
version: 2.
|
|
92
|
+
version: 2.64.0
|
|
93
93
|
contact:
|
|
94
94
|
name: API Support
|
|
95
95
|
|
|
@@ -1133,6 +1133,60 @@ paths:
|
|
|
1133
1133
|
# ============================================
|
|
1134
1134
|
|
|
1135
1135
|
/api/workflows:
|
|
1136
|
+
get:
|
|
1137
|
+
summary: List the caller's workflows
|
|
1138
|
+
description: |
|
|
1139
|
+
Cursor-paginated, **user-scoped** summary list of the caller's
|
|
1140
|
+
workflows, most-recent-first (`created_at DESC`, with an id
|
|
1141
|
+
tiebreaker for same-second rows). Each row is a **lightweight
|
|
1142
|
+
summary** (id / status / created_at + per-job type+status + a
|
|
1143
|
+
deliverable-output count) for list rendering — it deliberately does
|
|
1144
|
+
NOT inline per-operation `result_metadata` or output details (avoids
|
|
1145
|
+
N×M×K payload blow-up). Drill in via `GET /api/workflows/{id}/status`
|
|
1146
|
+
(per-op detail incl. `result_metadata`) and
|
|
1147
|
+
`GET /api/workflows/{id}/downloads` (deliverables). Rows persist until
|
|
1148
|
+
GDPR purge — workflows whose 24h download window has expired are still
|
|
1149
|
+
listed. Per ticket [`nLK1IO1k`](https://trello.com/c/nLK1IO1k) (A1).
|
|
1150
|
+
operationId: listWorkflows
|
|
1151
|
+
security: [{bearerAuth: []}, {sessionAuth: []}] # required (explicit 401 — user-scoped list, no anonymous access)
|
|
1152
|
+
x-identity-scoped: true # caller's own workflows — implicit identity-scoped, per ADR-0016 D3 (cf. credits/usage)
|
|
1153
|
+
tags:
|
|
1154
|
+
- Workflows
|
|
1155
|
+
parameters:
|
|
1156
|
+
- name: cursor
|
|
1157
|
+
in: query
|
|
1158
|
+
required: false
|
|
1159
|
+
description: |
|
|
1160
|
+
Opaque pagination cursor from a previous page's `next_cursor`.
|
|
1161
|
+
Omit (or send empty) for the first page; walk pages by passing
|
|
1162
|
+
each `next_cursor` until `is_truncated: false`. Encodes the
|
|
1163
|
+
`(created_at, id)` position — treat as opaque, do not parse.
|
|
1164
|
+
schema:
|
|
1165
|
+
type: string
|
|
1166
|
+
- name: limit
|
|
1167
|
+
in: query
|
|
1168
|
+
required: false
|
|
1169
|
+
description: Maximum rows per page (1-100; default 20).
|
|
1170
|
+
schema:
|
|
1171
|
+
type: integer
|
|
1172
|
+
minimum: 1
|
|
1173
|
+
maximum: 100
|
|
1174
|
+
default: 20
|
|
1175
|
+
responses:
|
|
1176
|
+
'200':
|
|
1177
|
+
description: Cursor-paginated summary list of the caller's workflows.
|
|
1178
|
+
content:
|
|
1179
|
+
application/json:
|
|
1180
|
+
schema:
|
|
1181
|
+
$ref: '#/components/schemas/WorkflowListSuccessEnvelope'
|
|
1182
|
+
'401':
|
|
1183
|
+
description: |
|
|
1184
|
+
Authentication required — anonymous callers cannot list
|
|
1185
|
+
workflows (the list is user-scoped).
|
|
1186
|
+
content:
|
|
1187
|
+
application/json:
|
|
1188
|
+
schema:
|
|
1189
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
1136
1190
|
post:
|
|
1137
1191
|
summary: Create a workflow
|
|
1138
1192
|
description: |
|
|
@@ -1146,11 +1200,20 @@ paths:
|
|
|
1146
1200
|
discriminated union with 4 leaves: `upload` /
|
|
1147
1201
|
`external_import` / `connection` / `job_output`).
|
|
1148
1202
|
- **Multi-input jobs** (merge, archive, image_watermark,
|
|
1149
|
-
custom_luma, audio_overlay):
|
|
1203
|
+
custom_luma, audio_overlay, audio_to_video, video_watermark):
|
|
1204
|
+
provide `inputs[]` with
|
|
1150
1205
|
`JobInputV2` entries. Each entry has its own `source`
|
|
1151
|
-
(
|
|
1152
|
-
|
|
1153
|
-
`
|
|
1206
|
+
(a `MultiInputSource` — the 3-leaf subset of `WorkflowSource`
|
|
1207
|
+
that **excludes `upload`**: `job_output` / `external_import` /
|
|
1208
|
+
`connection`), plus optional `role`
|
|
1209
|
+
(image_watermark, custom_luma, audio_overlay, audio_to_video,
|
|
1210
|
+
video_watermark) and
|
|
1211
|
+
`per_input_options`. To feed an uploaded file into a
|
|
1212
|
+
multi-input op, create a `passthrough` source job (single-input,
|
|
1213
|
+
`operations: [{type: passthrough}]`) and reference it from
|
|
1214
|
+
`inputs[].source` as `{type: job_output, from: <id>}` — see the
|
|
1215
|
+
`v2_merge_two_uploads` example and the `passthrough` bullet in
|
|
1216
|
+
`OperationType`.
|
|
1154
1217
|
|
|
1155
1218
|
Exactly one of `source` or `inputs` is required per job.
|
|
1156
1219
|
|
|
@@ -1182,10 +1245,31 @@ paths:
|
|
|
1182
1245
|
still receives the implicit `compress` — see above). Per
|
|
1183
1246
|
[ADR-0004](../docs/decisions/0004-job-shape.md).
|
|
1184
1247
|
|
|
1248
|
+
**Inert passthrough (lossless source jobs).** A single-input
|
|
1249
|
+
source job whose SOLE operation is `passthrough`
|
|
1250
|
+
(`operations: [{type: passthrough}]`) emits its source bytes
|
|
1251
|
+
UNCHANGED — no compression, no transformation, no Lambda. This
|
|
1252
|
+
is the EXPLICIT lossless path (built on the opt-out rule above:
|
|
1253
|
+
a non-empty `operations[]` without `compress`); it is distinct
|
|
1254
|
+
from `operations: []`, which KEEPS its implicit-compress meaning
|
|
1255
|
+
on a single-input upload job. The API **self-completes** a
|
|
1256
|
+
passthrough job at publish: the job's terminal output IS the
|
|
1257
|
+
upload's `{bucket, key}` unchanged, and `passthrough` is **never
|
|
1258
|
+
published to SNS** (no `ops-passthrough` queue; absent from the
|
|
1259
|
+
AsyncAPI routing enums). Its purpose is to bridge an uploaded
|
|
1260
|
+
file into a multi-input op (`merge` / `archive` /
|
|
1261
|
+
`image_watermark` / …): since `JobInputV2.source` excludes
|
|
1262
|
+
upload-direct, the upload becomes a `passthrough` source job
|
|
1263
|
+
that the downstream multi-input job references via
|
|
1264
|
+
`{type: job_output, from: <id>}` — preserving billing / DAG /
|
|
1265
|
+
lineage. Per ticket
|
|
1266
|
+
[`4som89Uh`](https://trello.com/c/4som89Uh).
|
|
1267
|
+
|
|
1185
1268
|
**Multi-input constraint:** multi-input jobs (`inputs[]`) must
|
|
1186
1269
|
have exactly one operation, which must be a multi-input type
|
|
1187
|
-
(`merge`, `archive`, `image_watermark`, `custom_luma`,
|
|
1188
|
-
`audio_overlay`).
|
|
1270
|
+
(`merge`, `archive`, `image_watermark`, `custom_luma`,
|
|
1271
|
+
`audio_overlay`, `audio_to_video`, or `video_watermark`).
|
|
1272
|
+
Chaining after a multi-input operation is
|
|
1189
1273
|
not valid — create a downstream `job_output`-sourced job
|
|
1190
1274
|
instead.
|
|
1191
1275
|
|
|
@@ -1210,7 +1294,14 @@ paths:
|
|
|
1210
1294
|
is deferred per ADR-0001 §1.7 row 5.
|
|
1211
1295
|
operationId: createWorkflow
|
|
1212
1296
|
security: [{}, {bearerAuth: []}, {sessionAuth: []}] # optional (anon-OK on free-tier baseline; auth required for tier-gated operations)
|
|
1213
|
-
x-identity-scoped:
|
|
1297
|
+
# x-identity-scoped: the request body dereferences identity-bound upload refs.
|
|
1298
|
+
# CARVE-OUT (ADR-0016 Amendment, nGYbgChX): cross-identity/anonymous upload refs
|
|
1299
|
+
# are 404 UPLOAD_NOT_FOUND IDOR/BOLA-masked (NEVER 403) so the response does not
|
|
1300
|
+
# reveal another user's upload exists — see the createWorkflow 404 response.
|
|
1301
|
+
# ADR-0016 D3's generic "identity-scoped -> 403" gloss over-claimed for uploads;
|
|
1302
|
+
# the runtime always 404-masked (CreateWorkflowCommandHandler.php:132-148).
|
|
1303
|
+
# (§D4 sample showed identity_scoped: false; B3 audit flipped to true per §D3 — SDK-acked 2026-05-28.)
|
|
1304
|
+
x-identity-scoped: true
|
|
1214
1305
|
tags:
|
|
1215
1306
|
- Workflow
|
|
1216
1307
|
requestBody:
|
|
@@ -1232,6 +1323,26 @@ paths:
|
|
|
1232
1323
|
options:
|
|
1233
1324
|
mode: lossy
|
|
1234
1325
|
quality: 80
|
|
1326
|
+
v2_flat_single_source:
|
|
1327
|
+
summary: >-
|
|
1328
|
+
V2 flat form (D0Gsri8V) — top-level source + operations,
|
|
1329
|
+
no jobs[] wrapper. Exactly equivalent to wrapping these in
|
|
1330
|
+
jobs:[{source,operations}]. Compress + a thumbnail derived
|
|
1331
|
+
from the original.
|
|
1332
|
+
value:
|
|
1333
|
+
source:
|
|
1334
|
+
type: upload
|
|
1335
|
+
file_id: "019539ab-1111-7000-8000-000000000001"
|
|
1336
|
+
operations:
|
|
1337
|
+
- type: compress
|
|
1338
|
+
options:
|
|
1339
|
+
mode: lossy
|
|
1340
|
+
quality: 80
|
|
1341
|
+
- type: thumbnail
|
|
1342
|
+
base: original
|
|
1343
|
+
options:
|
|
1344
|
+
width: 320
|
|
1345
|
+
height: 240
|
|
1235
1346
|
v2_chain_compress_thumbnail:
|
|
1236
1347
|
summary: V2 chained compress -> thumbnail (id required on first job)
|
|
1237
1348
|
value:
|
|
@@ -1251,16 +1362,28 @@ paths:
|
|
|
1251
1362
|
width: 320
|
|
1252
1363
|
height: 240
|
|
1253
1364
|
v2_merge_two_uploads:
|
|
1254
|
-
summary: V2 multi-input merge (two uploads
|
|
1365
|
+
summary: V2 multi-input merge (two uploads via passthrough source jobs)
|
|
1255
1366
|
value:
|
|
1256
1367
|
jobs:
|
|
1368
|
+
- id: src_a
|
|
1369
|
+
source:
|
|
1370
|
+
type: upload
|
|
1371
|
+
file_id: "019539ab-1111-7000-8000-000000000001"
|
|
1372
|
+
operations:
|
|
1373
|
+
- type: passthrough
|
|
1374
|
+
- id: src_b
|
|
1375
|
+
source:
|
|
1376
|
+
type: upload
|
|
1377
|
+
file_id: "019539ab-1111-7000-8000-000000000002"
|
|
1378
|
+
operations:
|
|
1379
|
+
- type: passthrough
|
|
1257
1380
|
- inputs:
|
|
1258
1381
|
- source:
|
|
1259
|
-
type:
|
|
1260
|
-
|
|
1382
|
+
type: job_output
|
|
1383
|
+
from: src_a
|
|
1261
1384
|
- source:
|
|
1262
|
-
type:
|
|
1263
|
-
|
|
1385
|
+
type: job_output
|
|
1386
|
+
from: src_b
|
|
1264
1387
|
operations:
|
|
1265
1388
|
- type: merge
|
|
1266
1389
|
v2_external_import:
|
|
@@ -1393,11 +1516,59 @@ paths:
|
|
|
1393
1516
|
max_parallel_long_form_jobs: 2
|
|
1394
1517
|
warnings: []
|
|
1395
1518
|
'400':
|
|
1396
|
-
description:
|
|
1519
|
+
description: |
|
|
1520
|
+
Bad request — malformed request body, or a workflow-DAG
|
|
1521
|
+
structural violation caught before semantic validation. The
|
|
1522
|
+
`ErrorEnvelope.error` field carries a specific machine code (per
|
|
1523
|
+
[`g8PPkbNu`](https://trello.com/c/g8PPkbNu) — narrower
|
|
1524
|
+
DAG-validation codes; HTTP statuses unchanged, only the code
|
|
1525
|
+
narrows). Conformance-safe: `error` is a free string (no enum).
|
|
1526
|
+
- `WORKFLOW_EDGE_REFERENCES_UNKNOWN_JOB` — a `workflow_edges`
|
|
1527
|
+
entry, a job-level `source`, or an `inputs[].source` references
|
|
1528
|
+
a job `ref`/`id` not present in the request (one code across
|
|
1529
|
+
all unknown-job-reference sites).
|
|
1530
|
+
- `CYCLIC_JOB_OUTPUT_SOURCE_GRAPH` — an implicit cycle in the
|
|
1531
|
+
`job_output` source graph, detected during effective-input-MIME
|
|
1532
|
+
resolution (distinct from explicit `workflow_edges` cycles,
|
|
1533
|
+
which are 422 `CYCLIC_WORKFLOW_EDGES`).
|
|
1534
|
+
- `RESERVED_JOB_ID_PATTERN` — a user-supplied job `id` matches the
|
|
1535
|
+
reserved `^job_\d+$` pattern **and is the SOLE validation
|
|
1536
|
+
failure**; mixed-violation requests keep the aggregated generic
|
|
1537
|
+
`BAD_REQUEST`.
|
|
1538
|
+
- `BAD_REQUEST` — malformed JSON / generic body validation, or any
|
|
1539
|
+
mixed/aggregated violation set.
|
|
1397
1540
|
content:
|
|
1398
1541
|
application/json:
|
|
1399
1542
|
schema:
|
|
1400
1543
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
1544
|
+
examples:
|
|
1545
|
+
edge_references_unknown_job:
|
|
1546
|
+
summary: A workflow reference points at a job that does not exist
|
|
1547
|
+
value:
|
|
1548
|
+
success: false
|
|
1549
|
+
error: "WORKFLOW_EDGE_REFERENCES_UNKNOWN_JOB"
|
|
1550
|
+
message: "A workflow reference points at a job that does not exist."
|
|
1551
|
+
message_key: "error.workflow_edge_references_unknown_job"
|
|
1552
|
+
reserved_job_id_pattern:
|
|
1553
|
+
summary: Sole violation — a job id uses the reserved pattern
|
|
1554
|
+
value:
|
|
1555
|
+
success: false
|
|
1556
|
+
error: "RESERVED_JOB_ID_PATTERN"
|
|
1557
|
+
message: "Job id must not use the reserved pattern (job_<number>)."
|
|
1558
|
+
message_key: "error.reserved_job_id_pattern"
|
|
1559
|
+
cyclic_job_output_source_graph:
|
|
1560
|
+
summary: Implicit cycle in the job_output source graph
|
|
1561
|
+
value:
|
|
1562
|
+
success: false
|
|
1563
|
+
error: "CYCLIC_JOB_OUTPUT_SOURCE_GRAPH"
|
|
1564
|
+
message: "Job output sources form a cycle."
|
|
1565
|
+
message_key: "error.cyclic_job_output_source_graph"
|
|
1566
|
+
bad_request_malformed:
|
|
1567
|
+
summary: Malformed JSON / generic or mixed-violation body
|
|
1568
|
+
value:
|
|
1569
|
+
success: false
|
|
1570
|
+
error: "BAD_REQUEST"
|
|
1571
|
+
message: "Invalid workflow request"
|
|
1401
1572
|
'403':
|
|
1402
1573
|
description: |
|
|
1403
1574
|
Forbidden — tier-quota or feature-tier restriction. Discriminated
|
|
@@ -1483,23 +1654,54 @@ paths:
|
|
|
1483
1654
|
error_type: "balance_exhausted"
|
|
1484
1655
|
required_action: "wait_for_renewal"
|
|
1485
1656
|
'404':
|
|
1486
|
-
description:
|
|
1657
|
+
description: |
|
|
1658
|
+
A referenced upload was not found. Returns the stable machine
|
|
1659
|
+
code `UPLOAD_NOT_FOUND` (`message_key: "upload.not_found"`).
|
|
1660
|
+
|
|
1661
|
+
Uploads are referenced via `jobs[].source` (a `WorkflowSource`
|
|
1662
|
+
with `type: upload`), including the `passthrough` source jobs
|
|
1663
|
+
that bridge an uploaded file into a multi-input operation —
|
|
1664
|
+
multi-input `inputs[].source` cannot reference an upload
|
|
1665
|
+
directly (`MultiInputSource` excludes the `upload` leaf).
|
|
1666
|
+
|
|
1667
|
+
**BOLA/IDOR existence-mask (deliberate, security-reasoned):**
|
|
1668
|
+
this 404 is ALSO returned when the upload exists but is owned
|
|
1669
|
+
by a different identity (or an anonymous workflow references an
|
|
1670
|
+
owned upload) — reported as not-found, **never 403**, so the
|
|
1671
|
+
response does not reveal that another user's upload exists.
|
|
1672
|
+
Consistent with the workflow-owner 404s on
|
|
1673
|
+
cancel / resume / get-workflow. Ownerless (anonymous-intake)
|
|
1674
|
+
uploads have no owner to violate and stay usable by any caller.
|
|
1675
|
+
See [ADR-0016](../docs/decisions/0016-per-endpoint-auth-identity-modeling.md)
|
|
1676
|
+
§"Amendment — upload IDOR mask".
|
|
1487
1677
|
content:
|
|
1488
1678
|
application/json:
|
|
1489
1679
|
schema:
|
|
1490
1680
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
1491
1681
|
example:
|
|
1492
1682
|
success: false
|
|
1493
|
-
error: "
|
|
1683
|
+
error: "UPLOAD_NOT_FOUND"
|
|
1684
|
+
message: "Upload not found"
|
|
1685
|
+
message_key: "upload.not_found"
|
|
1686
|
+
locale: "en-GB"
|
|
1494
1687
|
'422':
|
|
1495
1688
|
description: |
|
|
1496
1689
|
Semantically invalid request. One of:
|
|
1497
1690
|
|
|
1498
1691
|
- Generic validation error (unknown operation type, invalid
|
|
1499
|
-
option combinations,
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1692
|
+
option combinations, multi-input job with multiple
|
|
1693
|
+
operations) — returned as `ValidationErrorEnvelope`
|
|
1694
|
+
(`error_type: "validation_error"`, per ADR-0018).
|
|
1695
|
+
- Cyclic explicit `workflow_edges` (a cycle or self-edge in the
|
|
1696
|
+
edge DAG) — returned as a `ValidationErrorEnvelope` with
|
|
1697
|
+
`error_type: "validation_error"`, `error:
|
|
1698
|
+
"CYCLIC_WORKFLOW_EDGES"`, `message_key:
|
|
1699
|
+
"error.cyclic_workflow_edges"`, and a `details[0].field =
|
|
1700
|
+
"workflow_edges"` entry (per
|
|
1701
|
+
[`g8PPkbNu`](https://trello.com/c/g8PPkbNu)). Implicit
|
|
1702
|
+
`job_output` source-graph cycles and unknown-job references are
|
|
1703
|
+
narrower **400** codes (`CYCLIC_JOB_OUTPUT_SOURCE_GRAPH` /
|
|
1704
|
+
`WORKFLOW_EDGE_REFERENCES_UNKNOWN_JOB`) — see the 400 response.
|
|
1503
1705
|
- Feature-availability violation (workflow references one or
|
|
1504
1706
|
more `planned` / `experimental` / non-enabled-`beta`
|
|
1505
1707
|
features) — returned as `FeatureNotAvailableResponse`
|
|
@@ -1537,29 +1739,34 @@ paths:
|
|
|
1537
1739
|
or `always`. The compatibility-probe algorithm is
|
|
1538
1740
|
server-side and deliberately opaque per plan v5 §F8.2.
|
|
1539
1741
|
|
|
1540
|
-
|
|
1541
|
-
(
|
|
1542
|
-
`
|
|
1543
|
-
|
|
1544
|
-
`
|
|
1545
|
-
`
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
`
|
|
1552
|
-
|
|
1553
|
-
`
|
|
1554
|
-
|
|
1555
|
-
`
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1742
|
+
**Branch dispatch via the `error_type` discriminator** (per
|
|
1743
|
+
[ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md)).
|
|
1744
|
+
All four branches carry a required `error_type` enum, so the
|
|
1745
|
+
`oneOf` declares an explicit `discriminator`
|
|
1746
|
+
(`propertyName: error_type`):
|
|
1747
|
+
- `validation_error` → `ValidationErrorEnvelope` (carries
|
|
1748
|
+
`details[]`; the specific failure is in the `error` machine
|
|
1749
|
+
code — `INVALID_OPTIONS`, `REQUIRES_REENCODE`,
|
|
1750
|
+
`CYCLIC_WORKFLOW_EDGES`, …). The `REQUIRES_REENCODE` flavour
|
|
1751
|
+
(per I16-CONS) reuses this same envelope/`error_type` and is
|
|
1752
|
+
distinguished only by its `error` code.
|
|
1753
|
+
- `feature_not_available` → `FeatureNotAvailableResponse`
|
|
1754
|
+
(`violations[]`).
|
|
1755
|
+
- `processing_class_exceeds_band` →
|
|
1756
|
+
`ProcessingClassExceedsBandResponse` (`violations[]`).
|
|
1757
|
+
- `probe_pending` → `ProbePendingResponse` (`job_ref`, no
|
|
1758
|
+
`violations[]`/`details[]`), emitted when the probe-pending
|
|
1759
|
+
gate is on and a job's upload has not yet been probed.
|
|
1760
|
+
|
|
1761
|
+
The prior 422 shape is otherwise preserved unchanged — the
|
|
1762
|
+
`details[]` / `violations[]` / `job_ref` required-field shapes
|
|
1763
|
+
still hold, so structural duck-typing keeps working; the
|
|
1764
|
+
discriminator simply lets generated SDK clients dispatch on a
|
|
1765
|
+
single property instead of mis-firing `instanceOf` guards
|
|
1766
|
+
(camelCase-vs-snake_case). `error_type: validation_error` is a
|
|
1767
|
+
**new required field** on `ValidationErrorEnvelope` — the
|
|
1768
|
+
server populates it at every emission site (rollout coordinated
|
|
1769
|
+
with the API per ADR-0018).
|
|
1563
1770
|
headers:
|
|
1564
1771
|
Retry-After:
|
|
1565
1772
|
required: false
|
|
@@ -1583,11 +1790,19 @@ paths:
|
|
|
1583
1790
|
- $ref: '#/components/schemas/FeatureNotAvailableResponse'
|
|
1584
1791
|
- $ref: '#/components/schemas/ProcessingClassExceedsBandResponse'
|
|
1585
1792
|
- $ref: '#/components/schemas/ProbePendingResponse'
|
|
1793
|
+
discriminator:
|
|
1794
|
+
propertyName: error_type
|
|
1795
|
+
mapping:
|
|
1796
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
1797
|
+
feature_not_available: '#/components/schemas/FeatureNotAvailableResponse'
|
|
1798
|
+
processing_class_exceeds_band: '#/components/schemas/ProcessingClassExceedsBandResponse'
|
|
1799
|
+
probe_pending: '#/components/schemas/ProbePendingResponse'
|
|
1586
1800
|
examples:
|
|
1587
1801
|
validation_error:
|
|
1588
1802
|
summary: Generic validation error (legacy shape)
|
|
1589
1803
|
value:
|
|
1590
1804
|
success: false
|
|
1805
|
+
error_type: "validation_error"
|
|
1591
1806
|
error: "INVALID_OPTIONS"
|
|
1592
1807
|
details:
|
|
1593
1808
|
- operation: "compress"
|
|
@@ -1596,6 +1811,17 @@ paths:
|
|
|
1596
1811
|
- operation: "thumbnail"
|
|
1597
1812
|
option: "width"
|
|
1598
1813
|
message: "Required field"
|
|
1814
|
+
cyclic_workflow_edges:
|
|
1815
|
+
summary: Cyclic or self-referential explicit workflow_edges (g8PPkbNu)
|
|
1816
|
+
value:
|
|
1817
|
+
success: false
|
|
1818
|
+
error_type: "validation_error"
|
|
1819
|
+
error: "CYCLIC_WORKFLOW_EDGES"
|
|
1820
|
+
message: "Workflow edges form a cycle."
|
|
1821
|
+
message_key: "error.cyclic_workflow_edges"
|
|
1822
|
+
details:
|
|
1823
|
+
- field: "workflow_edges"
|
|
1824
|
+
message: "Workflow edges form a cycle."
|
|
1599
1825
|
feature_not_available:
|
|
1600
1826
|
summary: Workflow references planned features
|
|
1601
1827
|
value:
|
|
@@ -1663,6 +1889,7 @@ paths:
|
|
|
1663
1889
|
summary: merge.video re_encode_mode=never with incompatible inputs (I16-CONS)
|
|
1664
1890
|
value:
|
|
1665
1891
|
success: false
|
|
1892
|
+
error_type: "validation_error"
|
|
1666
1893
|
error: "REQUIRES_REENCODE"
|
|
1667
1894
|
details:
|
|
1668
1895
|
- operation: "merge"
|
|
@@ -2156,10 +2383,11 @@ paths:
|
|
|
2156
2383
|
|
|
2157
2384
|
**Per-tier private caching.** Per ADR-0002, the response is
|
|
2158
2385
|
per-caller; CDN-style public caching is NOT used. Clients
|
|
2159
|
-
revalidate via `ETag`
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2386
|
+
revalidate via the strong `ETag` — send `If-None-Match` on
|
|
2387
|
+
subsequent requests; the server returns `304 Not Modified` when
|
|
2388
|
+
the cached response is still fresh. The content `ETag` is the
|
|
2389
|
+
**sole** conditional validator (per `sUyA9ZXD`): `If-Modified-Since`
|
|
2390
|
+
is **not** honored and `Last-Modified` is informational only.
|
|
2163
2391
|
|
|
2164
2392
|
Cache-key composition (server-side): `user_tier + schema_version + capabilities_version + environment`.
|
|
2165
2393
|
|
|
@@ -2204,9 +2432,11 @@ paths:
|
|
|
2204
2432
|
in: header
|
|
2205
2433
|
required: false
|
|
2206
2434
|
description: |
|
|
2207
|
-
|
|
2208
|
-
`
|
|
2209
|
-
|
|
2435
|
+
**NOT honored for conditional `304` (per `sUyA9ZXD`).** The
|
|
2436
|
+
server ignores `If-Modified-Since`; the content `ETag`
|
|
2437
|
+
(`If-None-Match`) is the sole conditional validator. Retained
|
|
2438
|
+
for backward compatibility — sending it has no effect on
|
|
2439
|
+
freshness; use `If-None-Match` instead.
|
|
2210
2440
|
schema:
|
|
2211
2441
|
type: string
|
|
2212
2442
|
format: http-date
|
|
@@ -2215,7 +2445,7 @@ paths:
|
|
|
2215
2445
|
'200':
|
|
2216
2446
|
description: |
|
|
2217
2447
|
Operation schema (raw JSON, no ResponseEnvelope). Tier-scoped
|
|
2218
|
-
per caller; revalidate via `ETag`
|
|
2448
|
+
per caller; revalidate via the `ETag` (`If-None-Match`).
|
|
2219
2449
|
headers:
|
|
2220
2450
|
Cache-Control:
|
|
2221
2451
|
schema:
|
|
@@ -2238,8 +2468,10 @@ paths:
|
|
|
2238
2468
|
type: string
|
|
2239
2469
|
format: http-date
|
|
2240
2470
|
description: |
|
|
2241
|
-
HTTP-date of last response generation.
|
|
2242
|
-
|
|
2471
|
+
HTTP-date of last response generation. **Informational
|
|
2472
|
+
only** — NOT a conditional validator (the server does not
|
|
2473
|
+
honor `If-Modified-Since`, per `sUyA9ZXD`). Use the `ETag`
|
|
2474
|
+
(`If-None-Match`) for revalidation.
|
|
2243
2475
|
example: "Sun, 26 Apr 2026 09:00:00 GMT"
|
|
2244
2476
|
content:
|
|
2245
2477
|
application/json:
|
|
@@ -2300,10 +2532,10 @@ paths:
|
|
|
2300
2532
|
values: ["max", "crop", "scale"]
|
|
2301
2533
|
'304':
|
|
2302
2534
|
description: |
|
|
2303
|
-
Cached response is still fresh
|
|
2304
|
-
`
|
|
2305
|
-
|
|
2306
|
-
empty.
|
|
2535
|
+
Cached response is still fresh (the client's `If-None-Match`
|
|
2536
|
+
matched the current content `ETag`). Server emits `ETag` +
|
|
2537
|
+
(informational) `Last-Modified` headers. `If-Modified-Since`
|
|
2538
|
+
does not drive this response (per `sUyA9ZXD`). Body empty.
|
|
2307
2539
|
headers:
|
|
2308
2540
|
Cache-Control:
|
|
2309
2541
|
schema:
|
|
@@ -2655,22 +2887,7 @@ paths:
|
|
|
2655
2887
|
content:
|
|
2656
2888
|
application/json:
|
|
2657
2889
|
schema:
|
|
2658
|
-
|
|
2659
|
-
required:
|
|
2660
|
-
- success
|
|
2661
|
-
- data
|
|
2662
|
-
properties:
|
|
2663
|
-
success:
|
|
2664
|
-
type: boolean
|
|
2665
|
-
enum: [true]
|
|
2666
|
-
data:
|
|
2667
|
-
type: object
|
|
2668
|
-
description: |
|
|
2669
|
-
Empty object on logout success — preserves the
|
|
2670
|
-
`{ success: true, data: {...} }` envelope
|
|
2671
|
-
invariant from `info.description`. Logout has
|
|
2672
|
-
no return payload; the data slot is always
|
|
2673
|
-
`{}`.
|
|
2890
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
2674
2891
|
example:
|
|
2675
2892
|
success: true
|
|
2676
2893
|
data: {}
|
|
@@ -2703,208 +2920,1247 @@ paths:
|
|
|
2703
2920
|
schema:
|
|
2704
2921
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2705
2922
|
|
|
2706
|
-
|
|
2707
|
-
# EXTERNAL IMPORT ENDPOINT (ticket I10)
|
|
2708
|
-
# ============================================
|
|
2709
|
-
|
|
2710
|
-
/api/external-imports:
|
|
2923
|
+
/api/auth/register:
|
|
2711
2924
|
post:
|
|
2712
|
-
summary: Register a
|
|
2925
|
+
summary: Register a new account
|
|
2713
2926
|
description: |
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
**Availability:** `planned` — runtime depends on cross-repo
|
|
2726
|
-
ticket [`MbosYtJD`](https://trello.com/c/MbosYtJD) (Lambda team
|
|
2727
|
-
publishes capability manifest) and the
|
|
2728
|
-
[`POST /api/connections`](https://trello.com/c/MbosYtJD) vault
|
|
2729
|
-
endpoint shipping. The contract surface ships now (contract-first
|
|
2730
|
-
per ADR-0001 §1.3); the runtime endpoint returns `404` until
|
|
2731
|
-
cross-repo wiring lands.
|
|
2732
|
-
operationId: createExternalImport
|
|
2733
|
-
# Auth: REQUIRED defensively. Endpoint is availability:planned (no
|
|
2734
|
-
# controller yet); but the endpoint stores encrypted server-side
|
|
2735
|
-
# secrets (bearer URLs, passwords) and anon-callable secret
|
|
2736
|
-
# storage is almost certainly wrong. Locking the contract here
|
|
2737
|
-
# while we still own the source of truth; runtime tightens to
|
|
2738
|
-
# match when the controller lands. Per ADR-0016 / karen review
|
|
2739
|
-
# on yN309QVb-B2.
|
|
2740
|
-
security: [{bearerAuth: []}, {sessionAuth: []}]
|
|
2741
|
-
x-availability: planned
|
|
2927
|
+
Creates an unverified account from an email/password pair and
|
|
2928
|
+
dispatches a verification email. The account exists but cannot
|
|
2929
|
+
authenticate until the verification token is redeemed at
|
|
2930
|
+
`POST /api/auth/verify-email`.
|
|
2931
|
+
|
|
2932
|
+
Disposable / blocklisted email domains and other invalid-argument
|
|
2933
|
+
rejections collapse into a non-validation `422` envelope
|
|
2934
|
+
(`error: UNPROCESSABLE_ENTITY`, no `details[]`) — distinct from the
|
|
2935
|
+
structured `ValidationErrorEnvelope` returned for malformed input.
|
|
2936
|
+
operationId: registerUser
|
|
2937
|
+
security: [] # anonymous (sign-up entry point — no creds yet)
|
|
2742
2938
|
tags:
|
|
2743
|
-
-
|
|
2939
|
+
- Auth
|
|
2744
2940
|
requestBody:
|
|
2745
2941
|
required: true
|
|
2746
2942
|
content:
|
|
2747
2943
|
application/json:
|
|
2748
2944
|
schema:
|
|
2749
|
-
|
|
2945
|
+
type: object
|
|
2946
|
+
required:
|
|
2947
|
+
- email
|
|
2948
|
+
- password
|
|
2949
|
+
properties:
|
|
2950
|
+
email:
|
|
2951
|
+
type: string
|
|
2952
|
+
format: email
|
|
2953
|
+
maxLength: 254
|
|
2954
|
+
password:
|
|
2955
|
+
type: string
|
|
2956
|
+
minLength: 8
|
|
2957
|
+
maxLength: 128
|
|
2958
|
+
example:
|
|
2959
|
+
email: "user@example.com"
|
|
2960
|
+
password: "s3cretpw"
|
|
2750
2961
|
responses:
|
|
2751
2962
|
'201':
|
|
2752
|
-
description:
|
|
2963
|
+
description: |
|
|
2964
|
+
Account created. A verification email has been dispatched; the
|
|
2965
|
+
account cannot authenticate until verified.
|
|
2753
2966
|
content:
|
|
2754
2967
|
application/json:
|
|
2755
2968
|
schema:
|
|
2756
|
-
$ref: '#/components/schemas/
|
|
2969
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
2757
2970
|
example:
|
|
2758
2971
|
success: true
|
|
2759
|
-
data:
|
|
2760
|
-
external_source_id: "019539ab-2222-7000-8000-000000000001"
|
|
2761
|
-
expires_at: "2026-04-26T15:00:00Z"
|
|
2762
|
-
provider: "s3_presigned"
|
|
2972
|
+
data: {}
|
|
2763
2973
|
'400':
|
|
2764
|
-
description:
|
|
2974
|
+
description: Request body is invalid (malformed JSON, wrong field types, or body too large).
|
|
2765
2975
|
content:
|
|
2766
2976
|
application/json:
|
|
2767
2977
|
schema:
|
|
2768
2978
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2769
2979
|
example:
|
|
2770
2980
|
success: false
|
|
2771
|
-
error: "
|
|
2772
|
-
'403':
|
|
2773
|
-
description: |
|
|
2774
|
-
Forbidden — SSRF policy violation (private IP, loopback,
|
|
2775
|
-
cloud metadata endpoint), tier-quota restriction, or
|
|
2776
|
-
provider not enabled for caller's tier.
|
|
2777
|
-
content:
|
|
2778
|
-
application/json:
|
|
2779
|
-
schema:
|
|
2780
|
-
oneOf:
|
|
2781
|
-
- $ref: '#/components/schemas/TierRestrictionResponse'
|
|
2782
|
-
- $ref: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
2783
|
-
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
2981
|
+
error: "Request body is invalid."
|
|
2784
2982
|
'422':
|
|
2785
2983
|
description: |
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2984
|
+
Structured input validation failure (`ValidationErrorEnvelope`,
|
|
2985
|
+
`error_type: validation_error`).
|
|
2986
|
+
|
|
2987
|
+
A non-validation `422` (`error: UNPROCESSABLE_ENTITY`, no
|
|
2988
|
+
`details[]`) is also returned for disposable/blocklisted email
|
|
2989
|
+
domains.
|
|
2790
2990
|
content:
|
|
2791
2991
|
application/json:
|
|
2792
2992
|
schema:
|
|
2793
2993
|
oneOf:
|
|
2794
2994
|
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
2795
|
-
- $ref: '#/components/schemas/
|
|
2995
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
2996
|
+
discriminator:
|
|
2997
|
+
propertyName: error_type
|
|
2998
|
+
mapping:
|
|
2999
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
3000
|
+
unprocessable_entity: '#/components/schemas/AuthRejectionEnvelope'
|
|
3001
|
+
example:
|
|
3002
|
+
success: false
|
|
3003
|
+
error_type: validation_error
|
|
3004
|
+
error: "Validation failed"
|
|
3005
|
+
details:
|
|
3006
|
+
- field: "password"
|
|
3007
|
+
message: "This value is too short."
|
|
2796
3008
|
'429':
|
|
2797
|
-
description:
|
|
3009
|
+
description: |
|
|
3010
|
+
Rate limit exceeded (too many registration attempts in the
|
|
3011
|
+
current window). Carries a `Retry-After` response header per the
|
|
3012
|
+
convention established at `POST /api/auth/login`.
|
|
3013
|
+
headers:
|
|
3014
|
+
Retry-After:
|
|
3015
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3016
|
+
schema:
|
|
3017
|
+
type: integer
|
|
3018
|
+
minimum: 0
|
|
3019
|
+
example: 60
|
|
2798
3020
|
content:
|
|
2799
3021
|
application/json:
|
|
2800
3022
|
schema:
|
|
2801
3023
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2802
3024
|
'500':
|
|
2803
|
-
description: Internal server error
|
|
3025
|
+
description: Internal server error.
|
|
2804
3026
|
content:
|
|
2805
3027
|
application/json:
|
|
2806
3028
|
schema:
|
|
2807
3029
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2808
3030
|
|
|
2809
|
-
|
|
2810
|
-
# AUDIO WATERMARK DECODE
|
|
2811
|
-
# ============================================
|
|
2812
|
-
|
|
2813
|
-
/api/audio-watermark/decode:
|
|
3031
|
+
/api/auth/verify-email:
|
|
2814
3032
|
post:
|
|
2815
|
-
summary:
|
|
3033
|
+
summary: Verify an account email address
|
|
2816
3034
|
description: |
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
`
|
|
2821
|
-
`
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
**Tier-restricted.** This endpoint is `enterprise`-only. Free
|
|
2825
|
-
and `pro` callers receive a 403 `feature_tier_restricted`.
|
|
2826
|
-
Anonymous callers receive a 401.
|
|
2827
|
-
|
|
2828
|
-
**Scope: own watermarks only.** The decoder will refuse to
|
|
2829
|
-
extract from media the caller did not mark themselves
|
|
2830
|
-
(server-side audit log of watermark origin per
|
|
2831
|
-
`watermark_id`). Per spike S11 acceptance — this avoids the
|
|
2832
|
-
privacy implications of arbitrary-media decode while still
|
|
2833
|
-
serving the forensic-tracking use case (find a leaked asset
|
|
2834
|
-
on a third-party platform, decode the watermark, look up the
|
|
2835
|
-
distribution channel that received the marked copy).
|
|
2836
|
-
|
|
2837
|
-
**Rate-limited.** Request rate per caller is enforced
|
|
2838
|
-
independently from the workflow-create rate limit; abusive
|
|
2839
|
-
decode patterns flag for audit per [ADR-0001](../docs/decisions/0001-contract-first-availability.md).
|
|
2840
|
-
|
|
2841
|
-
**`availability: planned`** — operation Lambda + decode
|
|
2842
|
-
Lambda are cross-repo follow-up. The endpoint declaration
|
|
2843
|
-
ships now (contract-first); the runtime returns
|
|
2844
|
-
`feature_not_available` (422) until the Lambda lands. Per
|
|
2845
|
-
Tension 1 (ADR-0001 §1.3).
|
|
2846
|
-
operationId: decodeAudioWatermark
|
|
2847
|
-
security: [{bearerAuth: []}, {sessionAuth: []}] # required (explicit 401 in response set; tier-gated runtime call)
|
|
3035
|
+
Redeems a verification token emailed at registration time, marking
|
|
3036
|
+
the account verified so it can authenticate. A failed or expired
|
|
3037
|
+
verification collapses into a non-validation `422` envelope
|
|
3038
|
+
(no `details[]`) — distinct from the structured
|
|
3039
|
+
`ValidationErrorEnvelope` returned for malformed input.
|
|
3040
|
+
operationId: verifyEmail
|
|
3041
|
+
security: [] # anonymous (token-bearer — pre-authentication)
|
|
2848
3042
|
tags:
|
|
2849
|
-
-
|
|
2850
|
-
x-availability: planned
|
|
3043
|
+
- Auth
|
|
2851
3044
|
requestBody:
|
|
2852
3045
|
required: true
|
|
2853
3046
|
content:
|
|
2854
3047
|
application/json:
|
|
2855
3048
|
schema:
|
|
2856
|
-
|
|
3049
|
+
type: object
|
|
3050
|
+
required:
|
|
3051
|
+
- token
|
|
3052
|
+
properties:
|
|
3053
|
+
token:
|
|
3054
|
+
type: string
|
|
3055
|
+
minLength: 1
|
|
3056
|
+
maxLength: 128
|
|
3057
|
+
example:
|
|
3058
|
+
token: "8f3c1d9a4b2e6f0c1a7d5e9b3c2f4a6d"
|
|
2857
3059
|
responses:
|
|
2858
3060
|
'200':
|
|
2859
|
-
description:
|
|
3061
|
+
description: Email verified. The account can now authenticate.
|
|
2860
3062
|
content:
|
|
2861
3063
|
application/json:
|
|
2862
3064
|
schema:
|
|
2863
|
-
$ref: '#/components/schemas/
|
|
2864
|
-
|
|
2865
|
-
|
|
3065
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
3066
|
+
example:
|
|
3067
|
+
success: true
|
|
3068
|
+
data: {}
|
|
3069
|
+
'400':
|
|
3070
|
+
description: Request body is invalid (malformed JSON, wrong field types, or body too large).
|
|
2866
3071
|
content:
|
|
2867
3072
|
application/json:
|
|
2868
3073
|
schema:
|
|
2869
3074
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2870
|
-
|
|
3075
|
+
example:
|
|
3076
|
+
success: false
|
|
3077
|
+
error: "Request body is invalid."
|
|
3078
|
+
'422':
|
|
2871
3079
|
description: |
|
|
2872
|
-
|
|
2873
|
-
`
|
|
2874
|
-
|
|
3080
|
+
Structured input validation failure (`ValidationErrorEnvelope`,
|
|
3081
|
+
`error_type: validation_error`).
|
|
3082
|
+
|
|
3083
|
+
A non-validation `422` (`error: UNPROCESSABLE_ENTITY`, no
|
|
3084
|
+
`details[]`) is returned when the verification link is invalid
|
|
3085
|
+
or expired.
|
|
2875
3086
|
content:
|
|
2876
3087
|
application/json:
|
|
2877
3088
|
schema:
|
|
2878
3089
|
oneOf:
|
|
2879
|
-
- $ref: '#/components/schemas/
|
|
2880
|
-
- $ref: '#/components/schemas/
|
|
3090
|
+
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3091
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
2881
3092
|
discriminator:
|
|
2882
3093
|
propertyName: error_type
|
|
2883
3094
|
mapping:
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
3095
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
3096
|
+
unprocessable_entity: '#/components/schemas/AuthRejectionEnvelope'
|
|
3097
|
+
example:
|
|
3098
|
+
success: false
|
|
3099
|
+
error_type: validation_error
|
|
3100
|
+
error: "Validation failed"
|
|
3101
|
+
details:
|
|
3102
|
+
- field: "token"
|
|
3103
|
+
message: "This value should not be blank."
|
|
3104
|
+
'429':
|
|
2887
3105
|
description: |
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
3106
|
+
Rate limit exceeded (too many verification attempts in the
|
|
3107
|
+
current window). Carries a `Retry-After` response header.
|
|
3108
|
+
headers:
|
|
3109
|
+
Retry-After:
|
|
3110
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3111
|
+
schema:
|
|
3112
|
+
type: integer
|
|
3113
|
+
minimum: 0
|
|
3114
|
+
example: 60
|
|
2891
3115
|
content:
|
|
2892
3116
|
application/json:
|
|
2893
3117
|
schema:
|
|
2894
3118
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2895
|
-
'
|
|
2896
|
-
description:
|
|
2897
|
-
Either generic validation error
|
|
2898
|
-
(`ValidationErrorEnvelope`) or the operation is `planned`
|
|
2899
|
-
and the runtime Lambda has not yet shipped
|
|
2900
|
-
(`FeatureNotAvailableResponse` with `error_type:
|
|
2901
|
-
feature_not_available`).
|
|
3119
|
+
'500':
|
|
3120
|
+
description: Internal server error.
|
|
2902
3121
|
content:
|
|
2903
3122
|
application/json:
|
|
2904
3123
|
schema:
|
|
2905
|
-
|
|
2906
|
-
|
|
3124
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3125
|
+
|
|
3126
|
+
/api/auth/forgot-password:
|
|
3127
|
+
post:
|
|
3128
|
+
summary: Request a password-reset email
|
|
3129
|
+
description: |
|
|
3130
|
+
Dispatches a password-reset email if the address maps to an account.
|
|
3131
|
+
|
|
3132
|
+
**Anti-enumeration: this endpoint ALWAYS returns `200`**, even when
|
|
3133
|
+
the email does not correspond to any account. Callers cannot use the
|
|
3134
|
+
response to discover whether an account exists. The only non-success
|
|
3135
|
+
responses are structured input validation (`422`) and rate-limiting
|
|
3136
|
+
(`429`).
|
|
3137
|
+
operationId: forgotPassword
|
|
3138
|
+
security: [] # anonymous (account-recovery entry point)
|
|
3139
|
+
tags:
|
|
3140
|
+
- Auth
|
|
3141
|
+
requestBody:
|
|
3142
|
+
required: true
|
|
3143
|
+
content:
|
|
3144
|
+
application/json:
|
|
3145
|
+
schema:
|
|
3146
|
+
type: object
|
|
3147
|
+
required:
|
|
3148
|
+
- email
|
|
3149
|
+
properties:
|
|
3150
|
+
email:
|
|
3151
|
+
type: string
|
|
3152
|
+
format: email
|
|
3153
|
+
example:
|
|
3154
|
+
email: "user@example.com"
|
|
3155
|
+
responses:
|
|
3156
|
+
'200':
|
|
3157
|
+
description: |
|
|
3158
|
+
Request accepted. A reset email is dispatched only if the address
|
|
3159
|
+
maps to an account; the response shape is identical either way
|
|
3160
|
+
(anti-enumeration).
|
|
3161
|
+
content:
|
|
3162
|
+
application/json:
|
|
3163
|
+
schema:
|
|
3164
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
3165
|
+
example:
|
|
3166
|
+
success: true
|
|
3167
|
+
data: {}
|
|
3168
|
+
'400':
|
|
3169
|
+
description: Request body is invalid (malformed JSON, wrong field types, or body too large).
|
|
3170
|
+
content:
|
|
3171
|
+
application/json:
|
|
3172
|
+
schema:
|
|
3173
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3174
|
+
example:
|
|
3175
|
+
success: false
|
|
3176
|
+
error: "Request body is invalid."
|
|
3177
|
+
'422':
|
|
3178
|
+
description: Structured input validation failure (malformed `email`).
|
|
3179
|
+
content:
|
|
3180
|
+
application/json:
|
|
3181
|
+
schema:
|
|
3182
|
+
$ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3183
|
+
example:
|
|
3184
|
+
success: false
|
|
3185
|
+
error_type: validation_error
|
|
3186
|
+
error: "Validation failed"
|
|
3187
|
+
details:
|
|
3188
|
+
- field: "email"
|
|
3189
|
+
message: "This value is not a valid email address."
|
|
3190
|
+
'429':
|
|
3191
|
+
description: |
|
|
3192
|
+
Rate limit exceeded (too many reset requests in the current
|
|
3193
|
+
window). Carries a `Retry-After` response header.
|
|
3194
|
+
headers:
|
|
3195
|
+
Retry-After:
|
|
3196
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3197
|
+
schema:
|
|
3198
|
+
type: integer
|
|
3199
|
+
minimum: 0
|
|
3200
|
+
example: 60
|
|
3201
|
+
content:
|
|
3202
|
+
application/json:
|
|
3203
|
+
schema:
|
|
3204
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3205
|
+
|
|
3206
|
+
/api/auth/reset-password:
|
|
3207
|
+
post:
|
|
3208
|
+
summary: Reset a password using a reset token
|
|
3209
|
+
description: |
|
|
3210
|
+
Sets a new password using a token issued by
|
|
3211
|
+
`POST /api/auth/forgot-password`. An invalid or expired token is
|
|
3212
|
+
rejected with a flat `400 BAD_REQUEST` envelope — distinct from the
|
|
3213
|
+
structured `422 ValidationErrorEnvelope` returned for malformed input.
|
|
3214
|
+
operationId: resetPassword
|
|
3215
|
+
security: [] # anonymous (token-bearer — pre-authentication)
|
|
3216
|
+
tags:
|
|
3217
|
+
- Auth
|
|
3218
|
+
requestBody:
|
|
3219
|
+
required: true
|
|
3220
|
+
content:
|
|
3221
|
+
application/json:
|
|
3222
|
+
schema:
|
|
3223
|
+
type: object
|
|
3224
|
+
required:
|
|
3225
|
+
- token
|
|
3226
|
+
- password
|
|
3227
|
+
properties:
|
|
3228
|
+
token:
|
|
3229
|
+
type: string
|
|
3230
|
+
minLength: 1
|
|
3231
|
+
password:
|
|
3232
|
+
type: string
|
|
3233
|
+
minLength: 8
|
|
3234
|
+
example:
|
|
3235
|
+
token: "8f3c1d9a4b2e6f0c1a7d5e9b3c2f4a6d"
|
|
3236
|
+
password: "news3cretpw"
|
|
3237
|
+
responses:
|
|
3238
|
+
'200':
|
|
3239
|
+
description: Password reset. The caller can now authenticate with the new password.
|
|
3240
|
+
content:
|
|
3241
|
+
application/json:
|
|
3242
|
+
schema:
|
|
3243
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
3244
|
+
example:
|
|
3245
|
+
success: true
|
|
3246
|
+
data: {}
|
|
3247
|
+
'400':
|
|
3248
|
+
description: |
|
|
3249
|
+
The reset token is invalid, expired, or already consumed
|
|
3250
|
+
(`error: BAD_REQUEST`). Distinct from the structured `422`
|
|
3251
|
+
returned for malformed input.
|
|
3252
|
+
content:
|
|
3253
|
+
application/json:
|
|
3254
|
+
schema:
|
|
3255
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3256
|
+
example:
|
|
3257
|
+
success: false
|
|
3258
|
+
error: "BAD_REQUEST"
|
|
3259
|
+
'422':
|
|
3260
|
+
description: Structured input validation failure (e.g. blank `token`, short `password`).
|
|
3261
|
+
content:
|
|
3262
|
+
application/json:
|
|
3263
|
+
schema:
|
|
3264
|
+
$ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3265
|
+
example:
|
|
3266
|
+
success: false
|
|
3267
|
+
error_type: validation_error
|
|
3268
|
+
error: "Validation failed"
|
|
3269
|
+
details:
|
|
3270
|
+
- field: "password"
|
|
3271
|
+
message: "This value is too short."
|
|
3272
|
+
'429':
|
|
3273
|
+
description: |
|
|
3274
|
+
Rate limit exceeded (too many reset attempts in the current
|
|
3275
|
+
window). Carries a `Retry-After` response header.
|
|
3276
|
+
headers:
|
|
3277
|
+
Retry-After:
|
|
3278
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3279
|
+
schema:
|
|
3280
|
+
type: integer
|
|
3281
|
+
minimum: 0
|
|
3282
|
+
example: 60
|
|
3283
|
+
content:
|
|
3284
|
+
application/json:
|
|
3285
|
+
schema:
|
|
3286
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3287
|
+
|
|
3288
|
+
/api/auth/change-password:
|
|
3289
|
+
post:
|
|
3290
|
+
summary: Change the authenticated user's password
|
|
3291
|
+
description: |
|
|
3292
|
+
Changes the caller's password after re-verifying the current one.
|
|
3293
|
+
Requires an authenticated principal (session cookie or bearer token).
|
|
3294
|
+
|
|
3295
|
+
A wrong `current_password` is rejected with a flat `400 BAD_REQUEST`
|
|
3296
|
+
envelope — distinct from the structured `422 ValidationErrorEnvelope`
|
|
3297
|
+
returned for malformed input. `details[].field` values use the wire
|
|
3298
|
+
keys `current_password` / `new_password`.
|
|
3299
|
+
operationId: changePassword
|
|
3300
|
+
security: [{bearerAuth: []}, {sessionAuth: []}] # required (authenticated principal)
|
|
3301
|
+
x-identity-scoped: true # identity-bound (caller's own credential)
|
|
3302
|
+
tags:
|
|
3303
|
+
- Auth
|
|
3304
|
+
requestBody:
|
|
3305
|
+
required: true
|
|
3306
|
+
content:
|
|
3307
|
+
application/json:
|
|
3308
|
+
schema:
|
|
3309
|
+
type: object
|
|
3310
|
+
required:
|
|
3311
|
+
- current_password
|
|
3312
|
+
- new_password
|
|
3313
|
+
properties:
|
|
3314
|
+
current_password:
|
|
3315
|
+
type: string
|
|
3316
|
+
minLength: 1
|
|
3317
|
+
new_password:
|
|
3318
|
+
type: string
|
|
3319
|
+
minLength: 8
|
|
3320
|
+
example:
|
|
3321
|
+
current_password: "olds3cretpw"
|
|
3322
|
+
new_password: "news3cretpw"
|
|
3323
|
+
responses:
|
|
3324
|
+
'200':
|
|
3325
|
+
description: Password changed.
|
|
3326
|
+
content:
|
|
3327
|
+
application/json:
|
|
3328
|
+
schema:
|
|
3329
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
3330
|
+
example:
|
|
3331
|
+
success: true
|
|
3332
|
+
data: {}
|
|
3333
|
+
'400':
|
|
3334
|
+
description: |
|
|
3335
|
+
The supplied `current_password` is incorrect
|
|
3336
|
+
(`error: BAD_REQUEST`). Distinct from the structured `422`
|
|
3337
|
+
returned for malformed input.
|
|
3338
|
+
content:
|
|
3339
|
+
application/json:
|
|
3340
|
+
schema:
|
|
3341
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3342
|
+
example:
|
|
3343
|
+
success: false
|
|
3344
|
+
error: "BAD_REQUEST"
|
|
3345
|
+
'401':
|
|
3346
|
+
description: No authenticated principal.
|
|
3347
|
+
content:
|
|
3348
|
+
application/json:
|
|
3349
|
+
schema:
|
|
3350
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3351
|
+
example:
|
|
3352
|
+
success: false
|
|
3353
|
+
error: "AUTHENTICATION_REQUIRED"
|
|
3354
|
+
error_type: "authentication_required"
|
|
3355
|
+
message: "Authentication required."
|
|
3356
|
+
'404':
|
|
3357
|
+
description: |
|
|
3358
|
+
The authenticated principal no longer resolves to a stored user
|
|
3359
|
+
(`error: USER_NOT_FOUND`).
|
|
3360
|
+
content:
|
|
3361
|
+
application/json:
|
|
3362
|
+
schema:
|
|
3363
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3364
|
+
example:
|
|
3365
|
+
success: false
|
|
3366
|
+
error: "USER_NOT_FOUND"
|
|
3367
|
+
'422':
|
|
3368
|
+
description: Structured input validation failure (e.g. blank `current_password`, short `new_password`).
|
|
3369
|
+
content:
|
|
3370
|
+
application/json:
|
|
3371
|
+
schema:
|
|
3372
|
+
$ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3373
|
+
example:
|
|
3374
|
+
success: false
|
|
3375
|
+
error_type: validation_error
|
|
3376
|
+
error: "Validation failed"
|
|
3377
|
+
details:
|
|
3378
|
+
- field: "new_password"
|
|
3379
|
+
message: "This value is too short."
|
|
3380
|
+
'429':
|
|
3381
|
+
description: |
|
|
3382
|
+
Rate limit exceeded. Carries a `Retry-After` response header.
|
|
3383
|
+
headers:
|
|
3384
|
+
Retry-After:
|
|
3385
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3386
|
+
schema:
|
|
3387
|
+
type: integer
|
|
3388
|
+
minimum: 0
|
|
3389
|
+
example: 60
|
|
3390
|
+
content:
|
|
3391
|
+
application/json:
|
|
3392
|
+
schema:
|
|
3393
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3394
|
+
|
|
3395
|
+
/api/auth/confirm-email-change:
|
|
3396
|
+
post:
|
|
3397
|
+
summary: Confirm a pending email-address change
|
|
3398
|
+
description: |
|
|
3399
|
+
Redeems a token issued when the caller requested an email change via
|
|
3400
|
+
`PATCH /api/auth/profile`, applying the new address to the account.
|
|
3401
|
+
|
|
3402
|
+
Failure modes carry their own `error_type` discriminators
|
|
3403
|
+
(`token_not_found`, etc.) on the flat `ErrorEnvelope` shape and are
|
|
3404
|
+
distinct from the structured `422 ValidationErrorEnvelope` returned
|
|
3405
|
+
for malformed input.
|
|
3406
|
+
operationId: confirmEmailChange
|
|
3407
|
+
security: [] # anonymous (token-bearer — confirmation link)
|
|
3408
|
+
tags:
|
|
3409
|
+
- Auth
|
|
3410
|
+
requestBody:
|
|
3411
|
+
required: true
|
|
3412
|
+
content:
|
|
3413
|
+
application/json:
|
|
3414
|
+
schema:
|
|
3415
|
+
type: object
|
|
3416
|
+
required:
|
|
3417
|
+
- token
|
|
3418
|
+
properties:
|
|
3419
|
+
token:
|
|
3420
|
+
type: string
|
|
3421
|
+
minLength: 1
|
|
3422
|
+
example:
|
|
3423
|
+
token: "8f3c1d9a4b2e6f0c1a7d5e9b3c2f4a6d"
|
|
3424
|
+
responses:
|
|
3425
|
+
'200':
|
|
3426
|
+
description: Email address changed.
|
|
3427
|
+
content:
|
|
3428
|
+
application/json:
|
|
3429
|
+
schema:
|
|
3430
|
+
type: object
|
|
3431
|
+
required:
|
|
3432
|
+
- success
|
|
3433
|
+
- data
|
|
3434
|
+
properties:
|
|
3435
|
+
success:
|
|
3436
|
+
type: boolean
|
|
3437
|
+
enum: [true]
|
|
3438
|
+
data:
|
|
3439
|
+
type: object
|
|
3440
|
+
required:
|
|
3441
|
+
- message
|
|
3442
|
+
properties:
|
|
3443
|
+
message:
|
|
3444
|
+
type: string
|
|
3445
|
+
example:
|
|
3446
|
+
success: true
|
|
3447
|
+
data:
|
|
3448
|
+
message: "Email address has been changed successfully."
|
|
3449
|
+
'400':
|
|
3450
|
+
description: "Malformed or otherwise rejected confirmation (`error: BAD_REQUEST`)."
|
|
3451
|
+
content:
|
|
3452
|
+
application/json:
|
|
3453
|
+
schema:
|
|
3454
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3455
|
+
example:
|
|
3456
|
+
success: false
|
|
3457
|
+
error: "BAD_REQUEST"
|
|
3458
|
+
'404':
|
|
3459
|
+
description: |
|
|
3460
|
+
The confirmation token does not match any pending email change
|
|
3461
|
+
(`error: TOKEN_NOT_FOUND`, `error_type: token_not_found`).
|
|
3462
|
+
content:
|
|
3463
|
+
application/json:
|
|
3464
|
+
schema:
|
|
3465
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3466
|
+
example:
|
|
3467
|
+
success: false
|
|
3468
|
+
error: "TOKEN_NOT_FOUND"
|
|
3469
|
+
error_type: "token_not_found"
|
|
3470
|
+
'409':
|
|
3471
|
+
description: |
|
|
3472
|
+
The target email address was claimed by another account before
|
|
3473
|
+
confirmation (`error: EMAIL_TAKEN`).
|
|
3474
|
+
content:
|
|
3475
|
+
application/json:
|
|
3476
|
+
schema:
|
|
3477
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3478
|
+
example:
|
|
3479
|
+
success: false
|
|
3480
|
+
error: "EMAIL_TAKEN"
|
|
3481
|
+
'410':
|
|
3482
|
+
description: |
|
|
3483
|
+
The confirmation token has expired or was already used
|
|
3484
|
+
(`error: TOKEN_EXPIRED` or `TOKEN_USED`).
|
|
3485
|
+
content:
|
|
3486
|
+
application/json:
|
|
3487
|
+
schema:
|
|
3488
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3489
|
+
example:
|
|
3490
|
+
success: false
|
|
3491
|
+
error: "TOKEN_EXPIRED"
|
|
3492
|
+
'422':
|
|
3493
|
+
description: Structured input validation failure (e.g. blank `token`).
|
|
3494
|
+
content:
|
|
3495
|
+
application/json:
|
|
3496
|
+
schema:
|
|
3497
|
+
$ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3498
|
+
example:
|
|
3499
|
+
success: false
|
|
3500
|
+
error_type: validation_error
|
|
3501
|
+
error: "Validation failed"
|
|
3502
|
+
details:
|
|
3503
|
+
- field: "token"
|
|
3504
|
+
message: "This value should not be blank."
|
|
3505
|
+
'500':
|
|
3506
|
+
description: Internal server error.
|
|
3507
|
+
content:
|
|
3508
|
+
application/json:
|
|
3509
|
+
schema:
|
|
3510
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3511
|
+
|
|
3512
|
+
/api/auth/api-keys:
|
|
3513
|
+
post:
|
|
3514
|
+
summary: Create an API key for the authenticated user
|
|
3515
|
+
description: |
|
|
3516
|
+
Mints a new API key for the caller. The plaintext `key` is returned
|
|
3517
|
+
**once** in the creation response and never again — store it
|
|
3518
|
+
immediately.
|
|
3519
|
+
|
|
3520
|
+
**Session-auth only.** API-key management requires session
|
|
3521
|
+
authentication; callers authenticated via an API key (bearer) are
|
|
3522
|
+
rejected with `403`. This prevents an API key from minting further
|
|
3523
|
+
keys.
|
|
3524
|
+
operationId: createApiKey
|
|
3525
|
+
security: [{sessionAuth: []}] # session-only (API-key callers get 403)
|
|
3526
|
+
x-identity-scoped: true # identity-bound (caller's own keys)
|
|
3527
|
+
tags:
|
|
3528
|
+
- Auth
|
|
3529
|
+
requestBody:
|
|
3530
|
+
required: true
|
|
3531
|
+
content:
|
|
3532
|
+
application/json:
|
|
3533
|
+
schema:
|
|
3534
|
+
type: object
|
|
3535
|
+
required:
|
|
3536
|
+
- name
|
|
3537
|
+
properties:
|
|
3538
|
+
name:
|
|
3539
|
+
type: string
|
|
3540
|
+
minLength: 1
|
|
3541
|
+
maxLength: 50
|
|
3542
|
+
example:
|
|
3543
|
+
name: "ci-pipeline"
|
|
3544
|
+
responses:
|
|
3545
|
+
'201':
|
|
3546
|
+
description: |
|
|
3547
|
+
API key created. The plaintext `key` is shown here once and is
|
|
3548
|
+
never recoverable afterwards.
|
|
3549
|
+
content:
|
|
3550
|
+
application/json:
|
|
3551
|
+
schema:
|
|
3552
|
+
type: object
|
|
3553
|
+
required:
|
|
3554
|
+
- success
|
|
3555
|
+
- data
|
|
3556
|
+
properties:
|
|
3557
|
+
success:
|
|
3558
|
+
type: boolean
|
|
3559
|
+
enum: [true]
|
|
3560
|
+
data:
|
|
3561
|
+
type: object
|
|
3562
|
+
required:
|
|
3563
|
+
- id
|
|
3564
|
+
- name
|
|
3565
|
+
- prefix
|
|
3566
|
+
- created_at
|
|
3567
|
+
- key
|
|
3568
|
+
properties:
|
|
3569
|
+
id:
|
|
3570
|
+
$ref: '#/components/schemas/UuidV7'
|
|
3571
|
+
name:
|
|
3572
|
+
type: string
|
|
3573
|
+
prefix:
|
|
3574
|
+
type: string
|
|
3575
|
+
description: Short non-secret identifier prefix for the key.
|
|
3576
|
+
created_at:
|
|
3577
|
+
type: string
|
|
3578
|
+
format: date-time
|
|
3579
|
+
key:
|
|
3580
|
+
type: string
|
|
3581
|
+
description: Plaintext API key — shown once, never returned again.
|
|
3582
|
+
example:
|
|
3583
|
+
success: true
|
|
3584
|
+
data:
|
|
3585
|
+
id: "019539ab-1111-7000-8000-000000000010"
|
|
3586
|
+
name: "ci-pipeline"
|
|
3587
|
+
prefix: "gisl_ci"
|
|
3588
|
+
created_at: "2026-06-02T12:00:00Z"
|
|
3589
|
+
key: "gisl_ci_8f3c1d9a4b2e6f0c1a7d5e9b3c2f4a6d"
|
|
3590
|
+
'400':
|
|
3591
|
+
description: Request body is invalid (malformed JSON, wrong field types, or body too large).
|
|
3592
|
+
content:
|
|
3593
|
+
application/json:
|
|
3594
|
+
schema:
|
|
3595
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3596
|
+
example:
|
|
3597
|
+
success: false
|
|
3598
|
+
error: "Request body is invalid."
|
|
3599
|
+
'401':
|
|
3600
|
+
description: No authenticated principal.
|
|
3601
|
+
content:
|
|
3602
|
+
application/json:
|
|
3603
|
+
schema:
|
|
3604
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3605
|
+
example:
|
|
3606
|
+
success: false
|
|
3607
|
+
error: "AUTHENTICATION_REQUIRED"
|
|
3608
|
+
error_type: "authentication_required"
|
|
3609
|
+
message: "Authentication required."
|
|
3610
|
+
'403':
|
|
3611
|
+
description: |
|
|
3612
|
+
The caller authenticated via an API key (bearer); key management
|
|
3613
|
+
requires session authentication.
|
|
3614
|
+
content:
|
|
3615
|
+
application/json:
|
|
3616
|
+
schema:
|
|
3617
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3618
|
+
example:
|
|
3619
|
+
success: false
|
|
3620
|
+
error: "FORBIDDEN"
|
|
3621
|
+
'422':
|
|
3622
|
+
description: |
|
|
3623
|
+
Discriminated `oneOf` on `error_type` (per
|
|
3624
|
+
[ADR-0019](../docs/decisions/0019-auth-422-discriminated-oneof.md)):
|
|
3625
|
+
- **`validation_error`** (`ValidationErrorEnvelope`, with
|
|
3626
|
+
`details[]`) — structured input validation failure (e.g.
|
|
3627
|
+
blank or over-long `name`).
|
|
3628
|
+
- **`unprocessable_entity`** (`AuthRejectionEnvelope`, flat,
|
|
3629
|
+
no `details[]`) — domain rejection: duplicate or otherwise
|
|
3630
|
+
invalid key name (`error: UNPROCESSABLE_ENTITY`).
|
|
3631
|
+
content:
|
|
3632
|
+
application/json:
|
|
3633
|
+
schema:
|
|
3634
|
+
oneOf:
|
|
3635
|
+
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3636
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
3637
|
+
discriminator:
|
|
3638
|
+
propertyName: error_type
|
|
3639
|
+
mapping:
|
|
3640
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
3641
|
+
unprocessable_entity: '#/components/schemas/AuthRejectionEnvelope'
|
|
3642
|
+
example:
|
|
3643
|
+
success: false
|
|
3644
|
+
error_type: validation_error
|
|
3645
|
+
error: "Validation failed"
|
|
3646
|
+
details:
|
|
3647
|
+
- field: "name"
|
|
3648
|
+
message: "This value should not be blank."
|
|
3649
|
+
'429':
|
|
3650
|
+
description: |
|
|
3651
|
+
Rate limit exceeded. Carries a `Retry-After` response header.
|
|
3652
|
+
headers:
|
|
3653
|
+
Retry-After:
|
|
3654
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3655
|
+
schema:
|
|
3656
|
+
type: integer
|
|
3657
|
+
minimum: 0
|
|
3658
|
+
example: 60
|
|
3659
|
+
content:
|
|
3660
|
+
application/json:
|
|
3661
|
+
schema:
|
|
3662
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3663
|
+
'500':
|
|
3664
|
+
description: Internal server error.
|
|
3665
|
+
content:
|
|
3666
|
+
application/json:
|
|
3667
|
+
schema:
|
|
3668
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3669
|
+
|
|
3670
|
+
/api/auth/api-keys/{apiKeyId}:
|
|
3671
|
+
delete:
|
|
3672
|
+
summary: Revoke an API key for the authenticated user
|
|
3673
|
+
description: |
|
|
3674
|
+
Revokes (permanently deletes) one of the caller's API keys by id.
|
|
3675
|
+
|
|
3676
|
+
**Session-auth only.** Like key creation, revocation requires session
|
|
3677
|
+
authentication; callers authenticated via an API key (bearer) are
|
|
3678
|
+
rejected with `403`. This prevents an API key from revoking other
|
|
3679
|
+
keys.
|
|
3680
|
+
|
|
3681
|
+
**Identity-scoped.** The revoke is bound to the caller's own keys, so
|
|
3682
|
+
a key id that belongs to another account is indistinguishable from a
|
|
3683
|
+
non-existent one — both return `404`.
|
|
3684
|
+
operationId: revokeApiKey
|
|
3685
|
+
security: [{sessionAuth: []}] # session-only (API-key callers get 403)
|
|
3686
|
+
x-identity-scoped: true # identity-bound (caller's own keys)
|
|
3687
|
+
tags:
|
|
3688
|
+
- Auth
|
|
3689
|
+
parameters:
|
|
3690
|
+
- name: apiKeyId
|
|
3691
|
+
in: path
|
|
3692
|
+
required: true
|
|
3693
|
+
description: Identifier of the API key to revoke (from the creation response `id`).
|
|
3694
|
+
schema:
|
|
3695
|
+
$ref: '#/components/schemas/UuidV7'
|
|
3696
|
+
responses:
|
|
3697
|
+
'200':
|
|
3698
|
+
description: |
|
|
3699
|
+
API key revoked. The body is the shared empty-success envelope
|
|
3700
|
+
(`data` is an empty object, never `null`).
|
|
3701
|
+
content:
|
|
3702
|
+
application/json:
|
|
3703
|
+
schema:
|
|
3704
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
3705
|
+
example:
|
|
3706
|
+
success: true
|
|
3707
|
+
data: {}
|
|
3708
|
+
'400':
|
|
3709
|
+
description: The `apiKeyId` path segment is not a well-formed identifier.
|
|
3710
|
+
content:
|
|
3711
|
+
application/json:
|
|
3712
|
+
schema:
|
|
3713
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3714
|
+
example:
|
|
3715
|
+
success: false
|
|
3716
|
+
error: "INVALID_ID"
|
|
3717
|
+
'401':
|
|
3718
|
+
description: No authenticated principal.
|
|
3719
|
+
content:
|
|
3720
|
+
application/json:
|
|
3721
|
+
schema:
|
|
3722
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3723
|
+
example:
|
|
3724
|
+
success: false
|
|
3725
|
+
error: "AUTHENTICATION_REQUIRED"
|
|
3726
|
+
error_type: "authentication_required"
|
|
3727
|
+
message: "Authentication required."
|
|
3728
|
+
'403':
|
|
3729
|
+
description: |
|
|
3730
|
+
The caller authenticated via an API key (bearer); key management
|
|
3731
|
+
requires session authentication.
|
|
3732
|
+
content:
|
|
3733
|
+
application/json:
|
|
3734
|
+
schema:
|
|
3735
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3736
|
+
example:
|
|
3737
|
+
success: false
|
|
3738
|
+
error: "FORBIDDEN"
|
|
3739
|
+
'404':
|
|
3740
|
+
description: |
|
|
3741
|
+
No API key with this id belongs to the caller — the key does not
|
|
3742
|
+
exist, or it belongs to another account (identity-scoped, so the
|
|
3743
|
+
two cases are indistinguishable).
|
|
3744
|
+
content:
|
|
3745
|
+
application/json:
|
|
3746
|
+
schema:
|
|
3747
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3748
|
+
example:
|
|
3749
|
+
success: false
|
|
3750
|
+
error: "API_KEY_NOT_FOUND"
|
|
3751
|
+
'500':
|
|
3752
|
+
description: Internal server error.
|
|
3753
|
+
content:
|
|
3754
|
+
application/json:
|
|
3755
|
+
schema:
|
|
3756
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3757
|
+
|
|
3758
|
+
/api/auth/profile:
|
|
3759
|
+
patch:
|
|
3760
|
+
summary: Update the authenticated user's profile
|
|
3761
|
+
description: |
|
|
3762
|
+
Updates the caller's display name and/or email after re-verifying
|
|
3763
|
+
the current password. **At least one of `name` or `email` must be
|
|
3764
|
+
provided**, otherwise the request is rejected with `400`.
|
|
3765
|
+
|
|
3766
|
+
Changing the email does not apply it immediately — it triggers an
|
|
3767
|
+
email-change confirmation flow redeemed at
|
|
3768
|
+
`POST /api/auth/confirm-email-change`; the response signals this via
|
|
3769
|
+
`email_change_requested`.
|
|
3770
|
+
|
|
3771
|
+
Note the two distinct `422` shapes: structured input validation
|
|
3772
|
+
uses `ValidationErrorEnvelope`, whereas the `EMAIL_SAME` business
|
|
3773
|
+
rejection (new email equals current) is a flat `ErrorEnvelope` that
|
|
3774
|
+
is also a `422` but **not** the validation envelope.
|
|
3775
|
+
`details[].field` values use the wire keys `current_password` /
|
|
3776
|
+
`name` / `email`.
|
|
3777
|
+
operationId: updateProfile
|
|
3778
|
+
security: [{bearerAuth: []}, {sessionAuth: []}] # required (authenticated principal)
|
|
3779
|
+
x-identity-scoped: true # identity-bound (caller's own profile)
|
|
3780
|
+
tags:
|
|
3781
|
+
- Auth
|
|
3782
|
+
requestBody:
|
|
3783
|
+
required: true
|
|
3784
|
+
content:
|
|
3785
|
+
application/json:
|
|
3786
|
+
schema:
|
|
3787
|
+
type: object
|
|
3788
|
+
required:
|
|
3789
|
+
- current_password
|
|
3790
|
+
properties:
|
|
3791
|
+
current_password:
|
|
3792
|
+
type: string
|
|
3793
|
+
minLength: 1
|
|
3794
|
+
name:
|
|
3795
|
+
type:
|
|
3796
|
+
- string
|
|
3797
|
+
- "null"
|
|
3798
|
+
maxLength: 255
|
|
3799
|
+
email:
|
|
3800
|
+
type:
|
|
3801
|
+
- string
|
|
3802
|
+
- "null"
|
|
3803
|
+
format: email
|
|
3804
|
+
maxLength: 254
|
|
3805
|
+
example:
|
|
3806
|
+
current_password: "s3cretpw"
|
|
3807
|
+
name: "Jane Doe"
|
|
3808
|
+
email: "jane.new@example.com"
|
|
3809
|
+
responses:
|
|
3810
|
+
'200':
|
|
3811
|
+
description: |
|
|
3812
|
+
Profile updated. `name_changed` and `email_change_requested`
|
|
3813
|
+
flag which mutations took effect; an email change is pending
|
|
3814
|
+
confirmation rather than applied.
|
|
3815
|
+
content:
|
|
3816
|
+
application/json:
|
|
3817
|
+
schema:
|
|
3818
|
+
type: object
|
|
3819
|
+
required:
|
|
3820
|
+
- success
|
|
3821
|
+
- data
|
|
3822
|
+
properties:
|
|
3823
|
+
success:
|
|
3824
|
+
type: boolean
|
|
3825
|
+
enum: [true]
|
|
3826
|
+
data:
|
|
3827
|
+
type: object
|
|
3828
|
+
required:
|
|
3829
|
+
- updated
|
|
3830
|
+
properties:
|
|
3831
|
+
updated:
|
|
3832
|
+
type: boolean
|
|
3833
|
+
enum: [true]
|
|
3834
|
+
name_changed:
|
|
3835
|
+
type: boolean
|
|
3836
|
+
enum: [true]
|
|
3837
|
+
email_change_requested:
|
|
3838
|
+
type: boolean
|
|
3839
|
+
enum: [true]
|
|
3840
|
+
message:
|
|
3841
|
+
type: string
|
|
3842
|
+
example:
|
|
3843
|
+
success: true
|
|
3844
|
+
data:
|
|
3845
|
+
updated: true
|
|
3846
|
+
name_changed: true
|
|
3847
|
+
email_change_requested: true
|
|
3848
|
+
message: "A confirmation email has been sent to your new address."
|
|
3849
|
+
'400':
|
|
3850
|
+
description: |
|
|
3851
|
+
No mutable field was provided — at least one of `name` or `email`
|
|
3852
|
+
is required (`error: BAD_REQUEST`).
|
|
3853
|
+
content:
|
|
3854
|
+
application/json:
|
|
3855
|
+
schema:
|
|
3856
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3857
|
+
example:
|
|
3858
|
+
success: false
|
|
3859
|
+
error: "BAD_REQUEST"
|
|
3860
|
+
'401':
|
|
3861
|
+
description: No authenticated principal.
|
|
3862
|
+
content:
|
|
3863
|
+
application/json:
|
|
3864
|
+
schema:
|
|
3865
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3866
|
+
example:
|
|
3867
|
+
success: false
|
|
3868
|
+
error: "AUTHENTICATION_REQUIRED"
|
|
3869
|
+
error_type: "authentication_required"
|
|
3870
|
+
message: "Authentication required."
|
|
3871
|
+
'403':
|
|
3872
|
+
description: |
|
|
3873
|
+
The supplied `current_password` is incorrect
|
|
3874
|
+
(`error: INVALID_PASSWORD`).
|
|
3875
|
+
content:
|
|
3876
|
+
application/json:
|
|
3877
|
+
schema:
|
|
3878
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3879
|
+
example:
|
|
3880
|
+
success: false
|
|
3881
|
+
error: "INVALID_PASSWORD"
|
|
3882
|
+
'404':
|
|
3883
|
+
description: |
|
|
3884
|
+
The authenticated principal no longer resolves to a stored user
|
|
3885
|
+
(`error: USER_NOT_FOUND`).
|
|
3886
|
+
content:
|
|
3887
|
+
application/json:
|
|
3888
|
+
schema:
|
|
3889
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3890
|
+
example:
|
|
3891
|
+
success: false
|
|
3892
|
+
error: "USER_NOT_FOUND"
|
|
3893
|
+
'409':
|
|
3894
|
+
description: |
|
|
3895
|
+
The requested email is already claimed by another account
|
|
3896
|
+
(`error: EMAIL_TAKEN`).
|
|
3897
|
+
content:
|
|
3898
|
+
application/json:
|
|
3899
|
+
schema:
|
|
3900
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3901
|
+
example:
|
|
3902
|
+
success: false
|
|
3903
|
+
error: "EMAIL_TAKEN"
|
|
3904
|
+
'422':
|
|
3905
|
+
description: |
|
|
3906
|
+
Structured input validation failure (`ValidationErrorEnvelope`,
|
|
3907
|
+
`error_type: validation_error`).
|
|
3908
|
+
|
|
3909
|
+
A non-validation `422` (`error: EMAIL_SAME`, `error_type:
|
|
3910
|
+
email_same`, no `details[]`) is returned when the new email
|
|
3911
|
+
equals the current one — a flat `ErrorEnvelope` that is also
|
|
3912
|
+
`422` but **not** the validation envelope.
|
|
3913
|
+
content:
|
|
3914
|
+
application/json:
|
|
3915
|
+
schema:
|
|
3916
|
+
oneOf:
|
|
3917
|
+
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3918
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
3919
|
+
discriminator:
|
|
3920
|
+
propertyName: error_type
|
|
3921
|
+
mapping:
|
|
3922
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
3923
|
+
email_same: '#/components/schemas/AuthRejectionEnvelope'
|
|
3924
|
+
example:
|
|
3925
|
+
success: false
|
|
3926
|
+
error_type: validation_error
|
|
3927
|
+
error: "Validation failed"
|
|
3928
|
+
details:
|
|
3929
|
+
- field: "email"
|
|
3930
|
+
message: "This value is not a valid email address."
|
|
3931
|
+
'429':
|
|
3932
|
+
description: |
|
|
3933
|
+
Rate limit exceeded. Carries a `Retry-After` response header.
|
|
3934
|
+
headers:
|
|
3935
|
+
Retry-After:
|
|
3936
|
+
description: Seconds to wait before retrying. Delta-seconds (RFC 9110).
|
|
3937
|
+
schema:
|
|
3938
|
+
type: integer
|
|
3939
|
+
minimum: 0
|
|
3940
|
+
example: 60
|
|
3941
|
+
content:
|
|
3942
|
+
application/json:
|
|
3943
|
+
schema:
|
|
3944
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3945
|
+
'500':
|
|
3946
|
+
description: Internal server error.
|
|
3947
|
+
content:
|
|
3948
|
+
application/json:
|
|
3949
|
+
schema:
|
|
3950
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3951
|
+
|
|
3952
|
+
# ============================================
|
|
3953
|
+
# EXTERNAL IMPORT ENDPOINT (ticket I10)
|
|
3954
|
+
# ============================================
|
|
3955
|
+
|
|
3956
|
+
/api/external-imports:
|
|
3957
|
+
post:
|
|
3958
|
+
summary: Register a one-shot external import URL
|
|
3959
|
+
description: |
|
|
3960
|
+
Register a one-shot bearer URL (S3 presigned / GCS signed /
|
|
3961
|
+
Azure SAS / Dropbox shared link / public HTTPS) and receive an
|
|
3962
|
+
opaque `external_source_id` handle. Subsequent workflows
|
|
3963
|
+
reference the handle via `WorkflowSource` of `type:
|
|
3964
|
+
external_import`; the original URL + password are encrypted
|
|
3965
|
+
server-side and never returned in any response.
|
|
3966
|
+
|
|
3967
|
+
Per [ADR-0005](../docs/decisions/0005-external-sources.md) — see
|
|
3968
|
+
§"SSRF posture" for the 8 validation rules applied at
|
|
3969
|
+
registration time AND again at fetch time.
|
|
3970
|
+
|
|
3971
|
+
**Availability:** `planned` — runtime depends on cross-repo
|
|
3972
|
+
ticket [`MbosYtJD`](https://trello.com/c/MbosYtJD) (Lambda team
|
|
3973
|
+
publishes capability manifest) and the
|
|
3974
|
+
[`POST /api/connections`](https://trello.com/c/MbosYtJD) vault
|
|
3975
|
+
endpoint shipping. The contract surface ships now (contract-first
|
|
3976
|
+
per ADR-0001 §1.3); the runtime endpoint returns `404` until
|
|
3977
|
+
cross-repo wiring lands.
|
|
3978
|
+
operationId: createExternalImport
|
|
3979
|
+
# Auth: REQUIRED defensively. Endpoint is availability:planned (no
|
|
3980
|
+
# controller yet); but the endpoint stores encrypted server-side
|
|
3981
|
+
# secrets (bearer URLs, passwords) and anon-callable secret
|
|
3982
|
+
# storage is almost certainly wrong. Locking the contract here
|
|
3983
|
+
# while we still own the source of truth; runtime tightens to
|
|
3984
|
+
# match when the controller lands. Per ADR-0016 / karen review
|
|
3985
|
+
# on yN309QVb-B2.
|
|
3986
|
+
security: [{bearerAuth: []}, {sessionAuth: []}]
|
|
3987
|
+
x-availability: planned
|
|
3988
|
+
tags:
|
|
3989
|
+
- Upload
|
|
3990
|
+
requestBody:
|
|
3991
|
+
required: true
|
|
3992
|
+
content:
|
|
3993
|
+
application/json:
|
|
3994
|
+
schema:
|
|
3995
|
+
$ref: '#/components/schemas/ExternalImportRequest'
|
|
3996
|
+
responses:
|
|
3997
|
+
'201':
|
|
3998
|
+
description: External-import handle created.
|
|
3999
|
+
content:
|
|
4000
|
+
application/json:
|
|
4001
|
+
schema:
|
|
4002
|
+
$ref: '#/components/schemas/ExternalImportCreatedSuccessEnvelope'
|
|
4003
|
+
example:
|
|
4004
|
+
success: true
|
|
4005
|
+
data:
|
|
4006
|
+
external_source_id: "019539ab-2222-7000-8000-000000000001"
|
|
4007
|
+
expires_at: "2026-04-26T15:00:00Z"
|
|
4008
|
+
provider: "s3_presigned"
|
|
4009
|
+
'400':
|
|
4010
|
+
description: Validation failed (missing url, malformed URI, scheme not https).
|
|
4011
|
+
content:
|
|
4012
|
+
application/json:
|
|
4013
|
+
schema:
|
|
4014
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4015
|
+
example:
|
|
4016
|
+
success: false
|
|
4017
|
+
error: "URL is required and must be https://"
|
|
4018
|
+
'403':
|
|
4019
|
+
description: |
|
|
4020
|
+
Forbidden — SSRF policy violation (private IP, loopback,
|
|
4021
|
+
cloud metadata endpoint), tier-quota restriction, or
|
|
4022
|
+
provider not enabled for caller's tier.
|
|
4023
|
+
content:
|
|
4024
|
+
application/json:
|
|
4025
|
+
schema:
|
|
4026
|
+
oneOf:
|
|
4027
|
+
- $ref: '#/components/schemas/TierRestrictionResponse'
|
|
4028
|
+
- $ref: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
4029
|
+
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
4030
|
+
'422':
|
|
4031
|
+
description: |
|
|
4032
|
+
Unprocessable URL — DNS resolution failed, target
|
|
4033
|
+
unreachable, content-type / magic-byte sniff failed,
|
|
4034
|
+
`expires_at` already past, or `feature_not_available`
|
|
4035
|
+
(provider tagged `planned` for caller's tier).
|
|
4036
|
+
content:
|
|
4037
|
+
application/json:
|
|
4038
|
+
schema:
|
|
4039
|
+
oneOf:
|
|
4040
|
+
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
4041
|
+
- $ref: '#/components/schemas/FeatureNotAvailableResponse'
|
|
4042
|
+
discriminator:
|
|
4043
|
+
propertyName: error_type
|
|
4044
|
+
mapping:
|
|
4045
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
4046
|
+
feature_not_available: '#/components/schemas/FeatureNotAvailableResponse'
|
|
4047
|
+
'429':
|
|
4048
|
+
description: Rate limit exceeded
|
|
4049
|
+
content:
|
|
4050
|
+
application/json:
|
|
4051
|
+
schema:
|
|
4052
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4053
|
+
'500':
|
|
4054
|
+
description: Internal server error
|
|
4055
|
+
content:
|
|
4056
|
+
application/json:
|
|
4057
|
+
schema:
|
|
4058
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4059
|
+
|
|
4060
|
+
# ============================================
|
|
4061
|
+
# AUDIO WATERMARK DECODE
|
|
4062
|
+
# ============================================
|
|
4063
|
+
|
|
4064
|
+
/api/audio-watermark/decode:
|
|
4065
|
+
post:
|
|
4066
|
+
summary: Decode an embedded audio watermark
|
|
4067
|
+
description: |
|
|
4068
|
+
Extract the steganographic watermark embedded by a prior
|
|
4069
|
+
`audio_watermark` operation (per ticket
|
|
4070
|
+
[I20](https://trello.com/c/omiCq7Vn)). Pairs with the
|
|
4071
|
+
`audio_watermark` operation declared in
|
|
4072
|
+
`schemas/operations/audio_watermark.yaml` — the operation
|
|
4073
|
+
embeds; this endpoint decodes.
|
|
4074
|
+
|
|
4075
|
+
**Tier-restricted.** This endpoint is `enterprise`-only. Free
|
|
4076
|
+
and `pro` callers receive a 403 `feature_tier_restricted`.
|
|
4077
|
+
Anonymous callers receive a 401.
|
|
4078
|
+
|
|
4079
|
+
**Scope: own watermarks only.** The decoder will refuse to
|
|
4080
|
+
extract from media the caller did not mark themselves
|
|
4081
|
+
(server-side audit log of watermark origin per
|
|
4082
|
+
`watermark_id`). Per spike S11 acceptance — this avoids the
|
|
4083
|
+
privacy implications of arbitrary-media decode while still
|
|
4084
|
+
serving the forensic-tracking use case (find a leaked asset
|
|
4085
|
+
on a third-party platform, decode the watermark, look up the
|
|
4086
|
+
distribution channel that received the marked copy).
|
|
4087
|
+
|
|
4088
|
+
**Rate-limited.** Request rate per caller is enforced
|
|
4089
|
+
independently from the workflow-create rate limit; abusive
|
|
4090
|
+
decode patterns flag for audit per [ADR-0001](../docs/decisions/0001-contract-first-availability.md).
|
|
4091
|
+
|
|
4092
|
+
**`availability: planned`** — operation Lambda + decode
|
|
4093
|
+
Lambda are cross-repo follow-up. The endpoint declaration
|
|
4094
|
+
ships now (contract-first); the runtime returns
|
|
4095
|
+
`feature_not_available` (422) until the Lambda lands. Per
|
|
4096
|
+
Tension 1 (ADR-0001 §1.3).
|
|
4097
|
+
operationId: decodeAudioWatermark
|
|
4098
|
+
security: [{bearerAuth: []}, {sessionAuth: []}] # required (explicit 401 in response set; tier-gated runtime call)
|
|
4099
|
+
tags:
|
|
4100
|
+
- AudioWatermark
|
|
4101
|
+
x-availability: planned
|
|
4102
|
+
requestBody:
|
|
4103
|
+
required: true
|
|
4104
|
+
content:
|
|
4105
|
+
application/json:
|
|
4106
|
+
schema:
|
|
4107
|
+
$ref: '#/components/schemas/AudioWatermarkDecodeRequest'
|
|
4108
|
+
responses:
|
|
4109
|
+
'200':
|
|
4110
|
+
description: Watermark successfully extracted.
|
|
4111
|
+
content:
|
|
4112
|
+
application/json:
|
|
4113
|
+
schema:
|
|
4114
|
+
$ref: '#/components/schemas/AudioWatermarkDecodeResponse'
|
|
4115
|
+
'401':
|
|
4116
|
+
description: Authentication required (anonymous callers).
|
|
4117
|
+
content:
|
|
4118
|
+
application/json:
|
|
4119
|
+
schema:
|
|
4120
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4121
|
+
'403':
|
|
4122
|
+
description: |
|
|
4123
|
+
Tier insufficient (free / pro caller) — returned as
|
|
4124
|
+
`FeatureTierRestrictedResponse` with
|
|
4125
|
+
`error_type: feature_tier_restricted`. Per ADR-0001 §1.3.
|
|
4126
|
+
content:
|
|
4127
|
+
application/json:
|
|
4128
|
+
schema:
|
|
4129
|
+
oneOf:
|
|
4130
|
+
- $ref: '#/components/schemas/TierRestrictionResponse'
|
|
4131
|
+
- $ref: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
4132
|
+
discriminator:
|
|
4133
|
+
propertyName: error_type
|
|
4134
|
+
mapping:
|
|
4135
|
+
tier_restriction: '#/components/schemas/TierRestrictionResponse'
|
|
4136
|
+
feature_tier_restricted: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
4137
|
+
'404':
|
|
4138
|
+
description: |
|
|
4139
|
+
No watermark detected in the supplied asset, OR the
|
|
4140
|
+
detected watermark was not issued by this caller (the
|
|
4141
|
+
scope-limit applies — own-watermarks-only).
|
|
4142
|
+
content:
|
|
4143
|
+
application/json:
|
|
4144
|
+
schema:
|
|
4145
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4146
|
+
'422':
|
|
4147
|
+
description: |
|
|
4148
|
+
Either generic validation error
|
|
4149
|
+
(`ValidationErrorEnvelope`) or the operation is `planned`
|
|
4150
|
+
and the runtime Lambda has not yet shipped
|
|
4151
|
+
(`FeatureNotAvailableResponse` with `error_type:
|
|
4152
|
+
feature_not_available`).
|
|
4153
|
+
content:
|
|
4154
|
+
application/json:
|
|
4155
|
+
schema:
|
|
4156
|
+
oneOf:
|
|
4157
|
+
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
2907
4158
|
- $ref: '#/components/schemas/FeatureNotAvailableResponse'
|
|
4159
|
+
discriminator:
|
|
4160
|
+
propertyName: error_type
|
|
4161
|
+
mapping:
|
|
4162
|
+
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
4163
|
+
feature_not_available: '#/components/schemas/FeatureNotAvailableResponse'
|
|
2908
4164
|
'429':
|
|
2909
4165
|
description: |
|
|
2910
4166
|
Rate limit exceeded for decode requests. Decode
|
|
@@ -3191,6 +4447,7 @@ paths:
|
|
|
3191
4447
|
$ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
3192
4448
|
example:
|
|
3193
4449
|
success: false
|
|
4450
|
+
error_type: "validation_error"
|
|
3194
4451
|
error: "VALIDATION_FAILED"
|
|
3195
4452
|
details:
|
|
3196
4453
|
- field: "limit"
|
|
@@ -3373,6 +4630,37 @@ components:
|
|
|
3373
4630
|
success:
|
|
3374
4631
|
type: boolean
|
|
3375
4632
|
|
|
4633
|
+
EmptyData:
|
|
4634
|
+
type: object
|
|
4635
|
+
additionalProperties: false
|
|
4636
|
+
description: |
|
|
4637
|
+
No-payload success `data` slot — always an empty object `{}`,
|
|
4638
|
+
never `null`. Preserves the `{ success: true, data: {...} }`
|
|
4639
|
+
envelope invariant for operations that return no body (auth
|
|
4640
|
+
side-effect endpoints: register, logout, verify-email,
|
|
4641
|
+
forgot-password, reset-password, change-password). Modelled as
|
|
4642
|
+
an explicit empty object (not `type: null`) so every SDK
|
|
4643
|
+
generator emits a clean type — a `null`-typed `data` makes
|
|
4644
|
+
typescript-fetch import a non-existent `Null` model (TS2307)
|
|
4645
|
+
and crashes the python openapi-generator (`getPydanticType`).
|
|
4646
|
+
See ticket [VGqxO3fO](https://trello.com/c/VGqxO3fO).
|
|
4647
|
+
|
|
4648
|
+
EmptySuccessEnvelope:
|
|
4649
|
+
type: object
|
|
4650
|
+
description: |
|
|
4651
|
+
Success envelope for no-payload operations. `data` is always
|
|
4652
|
+
the empty object `{}`. Shared by the auth side-effect endpoints
|
|
4653
|
+
so their codegen stays clean and uniform.
|
|
4654
|
+
required:
|
|
4655
|
+
- success
|
|
4656
|
+
- data
|
|
4657
|
+
properties:
|
|
4658
|
+
success:
|
|
4659
|
+
type: boolean
|
|
4660
|
+
enum: [true]
|
|
4661
|
+
data:
|
|
4662
|
+
$ref: '#/components/schemas/EmptyData'
|
|
4663
|
+
|
|
3376
4664
|
ErrorEnvelope:
|
|
3377
4665
|
type: object
|
|
3378
4666
|
description: |
|
|
@@ -3429,6 +4717,17 @@ components:
|
|
|
3429
4717
|
would exceed the S3 multipart capacity cap. Pre-S3
|
|
3430
4718
|
server-side capacity gate; distinct from tier-quota
|
|
3431
4719
|
rejections (`upload_size_exceeds_tier`).
|
|
4720
|
+
|
|
4721
|
+
Workflow-create code (per ticket
|
|
4722
|
+
[`nGYbgChX`](https://trello.com/c/nGYbgChX) / sdks
|
|
4723
|
+
[`DRjIyMt9`](https://trello.com/c/DRjIyMt9)):
|
|
4724
|
+
- `UPLOAD_NOT_FOUND` (404) — a `POST /api/workflows` request
|
|
4725
|
+
references an upload that does not exist OR exists but is
|
|
4726
|
+
owned by a different identity (deliberate BOLA/IDOR
|
|
4727
|
+
existence-mask: reported as not-found, **never 403**, so the
|
|
4728
|
+
response does not reveal another user's upload exists).
|
|
4729
|
+
`message_key: "upload.not_found"`. See the createWorkflow
|
|
4730
|
+
404 response + ADR-0016 Amendment.
|
|
3432
4731
|
message:
|
|
3433
4732
|
type: string
|
|
3434
4733
|
description: |
|
|
@@ -3489,11 +4788,30 @@ components:
|
|
|
3489
4788
|
required:
|
|
3490
4789
|
- success
|
|
3491
4790
|
- error
|
|
4791
|
+
- error_type
|
|
3492
4792
|
- details
|
|
3493
4793
|
properties:
|
|
3494
4794
|
success:
|
|
3495
4795
|
type: boolean
|
|
3496
4796
|
enum: [false]
|
|
4797
|
+
error_type:
|
|
4798
|
+
type: string
|
|
4799
|
+
enum:
|
|
4800
|
+
- validation_error
|
|
4801
|
+
description: |
|
|
4802
|
+
Discriminator for the multi-branch 422 `oneOf` (per
|
|
4803
|
+
[ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md)).
|
|
4804
|
+
Always `validation_error` on this envelope. Added so the four
|
|
4805
|
+
422 branches (`ValidationErrorEnvelope`,
|
|
4806
|
+
`FeatureNotAvailableResponse`,
|
|
4807
|
+
`ProcessingClassExceedsBandResponse`, `ProbePendingResponse`)
|
|
4808
|
+
share a single discriminator property — generated SDK clients
|
|
4809
|
+
dispatch on `error_type` instead of structural `instanceOf`
|
|
4810
|
+
guards (which mis-fire on camelCase-vs-snake_case property
|
|
4811
|
+
names). Distinct from the `error` machine code below: `error`
|
|
4812
|
+
carries the specific failure code (`INVALID_OPTIONS`,
|
|
4813
|
+
`REQUIRES_REENCODE`, `CYCLIC_WORKFLOW_EDGES`, …) while
|
|
4814
|
+
`error_type` only names the envelope shape.
|
|
3497
4815
|
error:
|
|
3498
4816
|
type: string
|
|
3499
4817
|
description: |
|
|
@@ -3502,9 +4820,10 @@ components:
|
|
|
3502
4820
|
(per ticket I16-CONS — `merge.video` with
|
|
3503
4821
|
`re_encode_mode: never` and incompatible inputs; caller
|
|
3504
4822
|
resolves by switching to `re_encode_mode: auto` or `always`),
|
|
3505
|
-
`
|
|
3506
|
-
per
|
|
3507
|
-
|
|
4823
|
+
`CYCLIC_WORKFLOW_EDGES` (cyclic/self explicit `workflow_edges`,
|
|
4824
|
+
per `g8PPkbNu`), `VALIDATION_FAILED` (request/query-param
|
|
4825
|
+
validation failure per the project-wide convention — e.g.
|
|
4826
|
+
`GET /api/v2/credits/usage` invalid `limit`/`offset`).
|
|
3508
4827
|
SDKs duck-type on this field for typed error-branch helpers.
|
|
3509
4828
|
message:
|
|
3510
4829
|
type: string
|
|
@@ -3575,6 +4894,82 @@ components:
|
|
|
3575
4894
|
`ErrorEnvelope.message_params`. Excludes cost
|
|
3576
4895
|
numbers.
|
|
3577
4896
|
|
|
4897
|
+
AuthRejectionEnvelope:
|
|
4898
|
+
type: object
|
|
4899
|
+
description: |
|
|
4900
|
+
Flat **domain-rejection** 422 envelope for the auth surface — the
|
|
4901
|
+
non-validation branch of the auth-422 `oneOf` (per
|
|
4902
|
+
[ADR-0019](../docs/decisions/0019-auth-422-discriminated-oneof.md),
|
|
4903
|
+
the sibling adoption of [ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md)
|
|
4904
|
+
anticipated for non-workflow `oneOf` sites). Distinct from
|
|
4905
|
+
`ValidationErrorEnvelope`: it has **no `details[]`** (the
|
|
4906
|
+
rejection is a single business-rule failure, not a field-by-field
|
|
4907
|
+
validation report). Carries the same I26 localisation triple as
|
|
4908
|
+
`ErrorEnvelope`.
|
|
4909
|
+
|
|
4910
|
+
Returned alongside `ValidationErrorEnvelope` on the four auth
|
|
4911
|
+
endpoints that have a domain-reject 422 branch: `register`
|
|
4912
|
+
(disposable/blocklisted email), `verify-email` (invalid/expired
|
|
4913
|
+
token), `api-keys` POST (duplicate/invalid key name) — all
|
|
4914
|
+
`error: UNPROCESSABLE_ENTITY`, `error_type: unprocessable_entity`
|
|
4915
|
+
— and `profile` PATCH (`error: EMAIL_SAME`, `error_type:
|
|
4916
|
+
email_same`, new email equals current).
|
|
4917
|
+
|
|
4918
|
+
**`error_type` vs `error`** (per ADR-0018): `error_type` is the
|
|
4919
|
+
`oneOf` branch discriminator; the specific failure stays in the
|
|
4920
|
+
`error` machine code. `email_same` is retained as its own
|
|
4921
|
+
discriminator value (pre-existing wire shape) rather than folded
|
|
4922
|
+
into `unprocessable_entity`; both map to this envelope.
|
|
4923
|
+
required:
|
|
4924
|
+
- success
|
|
4925
|
+
- error
|
|
4926
|
+
- error_type
|
|
4927
|
+
properties:
|
|
4928
|
+
success:
|
|
4929
|
+
type: boolean
|
|
4930
|
+
enum: [false]
|
|
4931
|
+
error:
|
|
4932
|
+
type: string
|
|
4933
|
+
description: |
|
|
4934
|
+
Stable machine-readable failure code. `UNPROCESSABLE_ENTITY`
|
|
4935
|
+
for the generic auth domain rejections (register /
|
|
4936
|
+
verify-email / api-keys); `EMAIL_SAME` for the profile
|
|
4937
|
+
new-email-equals-current rejection. Canonical English; never
|
|
4938
|
+
localised. See `ErrorEnvelope.error`.
|
|
4939
|
+
error_type:
|
|
4940
|
+
type: string
|
|
4941
|
+
enum:
|
|
4942
|
+
- unprocessable_entity
|
|
4943
|
+
- email_same
|
|
4944
|
+
description: |
|
|
4945
|
+
Discriminator for the auth-422 `oneOf` (per ADR-0019). Names
|
|
4946
|
+
the envelope **shape**, not the failure — the failure is in
|
|
4947
|
+
`error`. `unprocessable_entity` for the generic flat auth
|
|
4948
|
+
rejections; `email_same` preserved as the profile-specific
|
|
4949
|
+
pre-existing wire value. Both resolve to this
|
|
4950
|
+
`AuthRejectionEnvelope`. Distinct from
|
|
4951
|
+
`ValidationErrorEnvelope.error_type` (`validation_error`),
|
|
4952
|
+
the other branch of the auth-422 `oneOf`.
|
|
4953
|
+
message:
|
|
4954
|
+
type: string
|
|
4955
|
+
description: |
|
|
4956
|
+
Human-readable message, localised per `Accept-Language`
|
|
4957
|
+
(fallback `en-GB`). Never parse for control flow. See
|
|
4958
|
+
`ErrorEnvelope.message`.
|
|
4959
|
+
message_key:
|
|
4960
|
+
type: string
|
|
4961
|
+
description: |
|
|
4962
|
+
Stable canonical lookup key for the message. Never localised.
|
|
4963
|
+
See `ErrorEnvelope.message_key`.
|
|
4964
|
+
locale:
|
|
4965
|
+
type: string
|
|
4966
|
+
description: BCP 47 locale tag echoing `Content-Language`. See `ErrorEnvelope.locale`.
|
|
4967
|
+
example: "en-GB"
|
|
4968
|
+
message_params:
|
|
4969
|
+
type: object
|
|
4970
|
+
additionalProperties: true
|
|
4971
|
+
description: Optional interpolation values for the localised `message`. See `ErrorEnvelope.message_params`.
|
|
4972
|
+
|
|
3578
4973
|
ReEncodeDecision:
|
|
3579
4974
|
type: string
|
|
3580
4975
|
description: |
|
|
@@ -3733,11 +5128,13 @@ components:
|
|
|
3733
5128
|
— this is the load-bearing invariant that makes the
|
|
3734
5129
|
primitive useful.
|
|
3735
5130
|
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
prose
|
|
3740
|
-
|
|
5131
|
+
All five role-based ops now declare `per_role_cardinality`
|
|
5132
|
+
(audio_to_video, video_watermark, audio_overlay,
|
|
5133
|
+
image_watermark, custom_luma — the last three migrated from
|
|
5134
|
+
prose-only by ticket oAP5BQOx). Absence of the block keeps
|
|
5135
|
+
prose-only semantics for any future role-based op until it
|
|
5136
|
+
migrates to the predicate form; the `JobInputRole` description
|
|
5137
|
+
remains the human-readable companion to the rules.
|
|
3741
5138
|
|
|
3742
5139
|
Per ticket [`SlluxMBN`](https://trello.com/c/SlluxMBN) /
|
|
3743
5140
|
ADR-0015. Consumed by `OperationInputRoleValidator` in
|
|
@@ -3776,8 +5173,10 @@ components:
|
|
|
3776
5173
|
# ============================================
|
|
3777
5174
|
#
|
|
3778
5175
|
# Per ADR-0005. WorkflowSource is the discriminated union consumed
|
|
3779
|
-
# by V2 JobDefinition.source
|
|
3780
|
-
#
|
|
5176
|
+
# by V2 single-input JobDefinition.source (wired by I12). Multi-input
|
|
5177
|
+
# JobInputV2.source consumes the narrower MultiInputSource (excludes
|
|
5178
|
+
# the upload leaf; see MultiInputSource). ExternalDestination is the
|
|
5179
|
+
# write-side counterpart.
|
|
3781
5180
|
|
|
3782
5181
|
UploadSource:
|
|
3783
5182
|
type: object
|
|
@@ -3869,8 +5268,11 @@ components:
|
|
|
3869
5268
|
|
|
3870
5269
|
WorkflowSource:
|
|
3871
5270
|
description: |
|
|
3872
|
-
Discriminated union of source leaves
|
|
3873
|
-
`
|
|
5271
|
+
Discriminated union of source leaves (all 4, including
|
|
5272
|
+
`upload`) consumed by V2 single-input `JobDefinition.source`.
|
|
5273
|
+
Multi-input `JobInputV2.source` consumes the narrower
|
|
5274
|
+
`MultiInputSource` (this union minus the `upload` leaf) — see
|
|
5275
|
+
that schema and `JobInputV2`. Per
|
|
3874
5276
|
[ADR-0005](../docs/decisions/0005-external-sources.md) +
|
|
3875
5277
|
[ADR-0004](../docs/decisions/0004-job-shape.md).
|
|
3876
5278
|
|
|
@@ -3889,6 +5291,30 @@ components:
|
|
|
3889
5291
|
external_import: '#/components/schemas/ExternalImportToken'
|
|
3890
5292
|
connection: '#/components/schemas/ConnectionSource'
|
|
3891
5293
|
|
|
5294
|
+
MultiInputSource:
|
|
5295
|
+
description: |
|
|
5296
|
+
Discriminated union of source leaves consumed by `JobInputV2.source`
|
|
5297
|
+
(multi-input operation inputs). A 3-leaf subset of `WorkflowSource`
|
|
5298
|
+
that **excludes the `upload` leaf** — uploads cannot be referenced
|
|
5299
|
+
directly inside a multi-input `inputs[]` array. An uploaded file
|
|
5300
|
+
that must feed a multi-input operation enters via a `passthrough`
|
|
5301
|
+
source job (a single-input job with `operations: [{type: passthrough}]`)
|
|
5302
|
+
and is referenced here as `{ type: job_output, from: <id> }`, which
|
|
5303
|
+
preserves billing / DAG / lineage. Structured like the `ExternalSource`
|
|
5304
|
+
subset alias but retaining `job_output` (the passthrough bridge).
|
|
5305
|
+
Per ticket [`4som89Uh`](https://trello.com/c/4som89Uh) + ADR-0004 /
|
|
5306
|
+
ADR-0005.
|
|
5307
|
+
oneOf:
|
|
5308
|
+
- $ref: '#/components/schemas/JobOutputSource'
|
|
5309
|
+
- $ref: '#/components/schemas/ExternalImportToken'
|
|
5310
|
+
- $ref: '#/components/schemas/ConnectionSource'
|
|
5311
|
+
discriminator:
|
|
5312
|
+
propertyName: type
|
|
5313
|
+
mapping:
|
|
5314
|
+
job_output: '#/components/schemas/JobOutputSource'
|
|
5315
|
+
external_import: '#/components/schemas/ExternalImportToken'
|
|
5316
|
+
connection: '#/components/schemas/ConnectionSource'
|
|
5317
|
+
|
|
3892
5318
|
ExternalSource:
|
|
3893
5319
|
description: |
|
|
3894
5320
|
Subset alias of `WorkflowSource` that excludes uploads and
|
|
@@ -4208,6 +5634,7 @@ components:
|
|
|
4208
5634
|
|
|
4209
5635
|
CreditsBalanceSuccessEnvelope:
|
|
4210
5636
|
type: object
|
|
5637
|
+
additionalProperties: false
|
|
4211
5638
|
required:
|
|
4212
5639
|
- success
|
|
4213
5640
|
- data
|
|
@@ -4378,6 +5805,7 @@ components:
|
|
|
4378
5805
|
|
|
4379
5806
|
CreditsUsageSuccessEnvelope:
|
|
4380
5807
|
type: object
|
|
5808
|
+
additionalProperties: false
|
|
4381
5809
|
required:
|
|
4382
5810
|
- success
|
|
4383
5811
|
- data
|
|
@@ -4535,6 +5963,7 @@ components:
|
|
|
4535
5963
|
|
|
4536
5964
|
WorkflowCancelSuccessEnvelope:
|
|
4537
5965
|
type: object
|
|
5966
|
+
additionalProperties: false
|
|
4538
5967
|
required:
|
|
4539
5968
|
- success
|
|
4540
5969
|
- data
|
|
@@ -4570,6 +5999,7 @@ components:
|
|
|
4570
5999
|
|
|
4571
6000
|
WorkflowResumeSuccessEnvelope:
|
|
4572
6001
|
type: object
|
|
6002
|
+
additionalProperties: false
|
|
4573
6003
|
required:
|
|
4574
6004
|
- success
|
|
4575
6005
|
- data
|
|
@@ -4988,13 +6418,12 @@ components:
|
|
|
4988
6418
|
with a generic schema error. This envelope is the structured
|
|
4989
6419
|
rejection.
|
|
4990
6420
|
|
|
4991
|
-
The 422 response is delivered alongside `ValidationErrorEnvelope
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
`POST /api/workflows`.
|
|
6421
|
+
The 422 response is delivered alongside `ValidationErrorEnvelope`,
|
|
6422
|
+
`ProcessingClassExceedsBandResponse`, and `ProbePendingResponse`
|
|
6423
|
+
via a `oneOf` with an explicit `error_type` discriminator (per
|
|
6424
|
+
[ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md));
|
|
6425
|
+
this branch's discriminator value is `feature_not_available`. See
|
|
6426
|
+
the 422 response on `POST /api/workflows`.
|
|
4998
6427
|
allOf:
|
|
4999
6428
|
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
5000
6429
|
- type: object
|
|
@@ -5037,12 +6466,13 @@ components:
|
|
|
5037
6466
|
|
|
5038
6467
|
Delivered alongside `ValidationErrorEnvelope`,
|
|
5039
6468
|
`FeatureNotAvailableResponse`, and
|
|
5040
|
-
`ProcessingClassExceedsBandResponse` via the
|
|
5041
|
-
the 422 response
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
6469
|
+
`ProcessingClassExceedsBandResponse` via the discriminated
|
|
6470
|
+
`oneOf` on the 422 response (per
|
|
6471
|
+
[ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md)) —
|
|
6472
|
+
this branch's `error_type` discriminator value is `probe_pending`;
|
|
6473
|
+
it also remains the only branch carrying `job_ref` and neither
|
|
6474
|
+
`details` (`ValidationErrorEnvelope`) nor `violations` (the other
|
|
6475
|
+
two typed envelopes).
|
|
5046
6476
|
allOf:
|
|
5047
6477
|
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
5048
6478
|
- type: object
|
|
@@ -5238,10 +6668,12 @@ components:
|
|
|
5238
6668
|
envelope is 422, not an extension of `TierRestrictionResponse`
|
|
5239
6669
|
on 403).
|
|
5240
6670
|
|
|
5241
|
-
Delivered on `POST /api/workflows` 422 in a
|
|
5242
|
-
alongside `ValidationErrorEnvelope`
|
|
5243
|
-
`
|
|
5244
|
-
|
|
6671
|
+
Delivered on `POST /api/workflows` 422 in a discriminated `oneOf`
|
|
6672
|
+
alongside `ValidationErrorEnvelope`, `FeatureNotAvailableResponse`,
|
|
6673
|
+
and `ProbePendingResponse` (per
|
|
6674
|
+
[ADR-0018](../docs/decisions/0018-universal-422-error-type-discriminator.md)) —
|
|
6675
|
+
this branch's `error_type` discriminator value is
|
|
6676
|
+
`processing_class_exceeds_band`.
|
|
5245
6677
|
allOf:
|
|
5246
6678
|
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
5247
6679
|
- type: object
|
|
@@ -5462,20 +6894,22 @@ components:
|
|
|
5462
6894
|
Available operation types:
|
|
5463
6895
|
- compress: Reduce file size (images, audio, video, documents)
|
|
5464
6896
|
- thumbnail: Legacy thumbnail value. Generates a preview image
|
|
5465
|
-
for any media type via a single Lambda.
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
below
|
|
6897
|
+
for any media type via a single Lambda. Retained as a valid
|
|
6898
|
+
routing target during the migration window, but the
|
|
6899
|
+
compression_api publisher no longer emits it — it now resolves
|
|
6900
|
+
the per-media sub-type value below on the SNS `operation_type`
|
|
6901
|
+
attribute. The payload `operation_type` field stays `thumbnail`.
|
|
5469
6902
|
- thumbnail_image: Image thumbnail sub-type. Backed by a dedicated
|
|
5470
|
-
Rust image Lambda.
|
|
6903
|
+
Rust image Lambda. Emitted by the publisher for image inputs.
|
|
5471
6904
|
- thumbnail_video: Video thumbnail sub-type. Backed by a dedicated
|
|
5472
|
-
FFmpeg Lambda.
|
|
6905
|
+
FFmpeg Lambda. Emitted by the publisher for video inputs.
|
|
5473
6906
|
- thumbnail_document: PDF/EPUB thumbnail sub-type. Backed by a
|
|
5474
|
-
dedicated Ghostscript Lambda.
|
|
6907
|
+
dedicated Ghostscript Lambda. Emitted by the publisher for
|
|
6908
|
+
PDF/EPUB inputs.
|
|
5475
6909
|
- thumbnail_office: Office document (DOCX/XLSX/PPTX/ODT/ODS/ODP)
|
|
5476
6910
|
thumbnail sub-type. Backed by a dedicated LibreOffice Lambda.
|
|
5477
|
-
|
|
5478
|
-
- image_watermark: Image overlay
|
|
6911
|
+
Emitted by the publisher for office-document inputs.
|
|
6912
|
+
- image_watermark: Image overlay onto a base IMAGE asset. Multi-input (Path B with role: base + overlay). Each input is a `MultiInputSource` (external_import / connection / job_output — no upload-direct); an uploaded base or overlay enters via a `passthrough` source job referenced by `job_output`. Stable for static-image bases (jpeg/png/webp); animated GIF is advertised as `planned` via the `image_gif` mime_group — dispatch returns `feature_not_available` (422) until Lambda support ships. Video bases are NOT supported by image_watermark — use the dedicated `video_watermark` operation per ADR-0013. Per ADR-0004 + I4-CONS + I5 (Trello AKZiOXnd).
|
|
5479
6913
|
- text_watermark: Text overlay rendered onto an image (Liberation Sans). Single-input. Per ADR-0004 + I4-CONS.
|
|
5480
6914
|
- merge: Concatenate/combine multiple files into one (images, video, audio). Multi-input. Image inputs merge into animated GIF or slideshow video; image collage/grid and PDF concatenation are not supported by the V1 Lambda.
|
|
5481
6915
|
- archive: Bundle files into ZIP/tar.gz (all types). Multi-input.
|
|
@@ -5483,10 +6917,11 @@ components:
|
|
|
5483
6917
|
- custom_luma: Apply a caller-uploaded luma matte to a base video for a custom luma-matte transition effect. Multi-input (`role: base` + `role: transition_mask`). `availability: planned` + `required_tier: pro`; dispatch returns `feature_not_available` (422) until Lambda ships. Distinct from FFmpeg `xfade=custom` (which is an expression, not an operation). Per ticket I29 (Trello EPUE5Vs1).
|
|
5484
6918
|
- audio_overlay: Mix a secondary audio asset over a primary audio or video base (DJ tags, podcast intros/outros, station IDs, jingles). Multi-input (`role: base` + `role: overlay`). `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. **NOT** the same as `audio_watermark` — that operation is steganographic (imperceptible identifier embedded for ownership tracking), tracked separately by I20. Per ticket I19 (Trello Xr3Z4GBF).
|
|
5485
6919
|
- audio_watermark: Embed a steganographic forensic watermark into an audio asset (or a video's audio track) — Cinavia / Resemble PerTh territory. Single-input. `availability: planned` + `required_tier: enterprise`; dispatch returns `feature_not_available` (422) until Lambda ships. Pairs with `POST /api/audio-watermark/decode` for own-watermarks-only extraction. Per ticket I20 (Trello omiCq7Vn).
|
|
5486
|
-
- audio_to_video: Produce a video from an audio input plus an OPTIONAL still image overlay. Multi-input role-based with the first OPTIONAL role on the contract (`role: base` audio required, `role: overlay` image 0..1 — see `per_role_cardinality`). When overlay is omitted, the video uses a solid background colour. `availability:
|
|
5487
|
-
- video_watermark: Apply an image overlay onto a base video via FFmpeg's `overlay` filter. Multi-input role-based (`role: base` video + `role: overlay` image, exactly one of each per `per_role_cardinality`). Re-encode required; audio stream-copy passthrough. Distinct from `image_watermark` (pure-Rust/image-only). `availability:
|
|
6920
|
+
- audio_to_video: Produce a video from an audio input plus an OPTIONAL still image overlay. Multi-input role-based with the first OPTIONAL role on the contract (`role: base` audio required, `role: overlay` image 0..1 — see `per_role_cardinality`). When overlay is omitted, the video uses a solid background colour. `availability: beta` (Wave A — Lambda backend live; opt-in, MUST NOT return `feature_not_available`; may change with notice). Per ticket [`SlluxMBN`](https://trello.com/c/SlluxMBN) + ADR-0015 (introduces `per_role_cardinality` vocab).
|
|
6921
|
+
- video_watermark: Apply an image overlay onto a base video via FFmpeg's `overlay` filter. Multi-input role-based (`role: base` video + `role: overlay` image, exactly one of each per `per_role_cardinality`). Re-encode required; audio stream-copy passthrough. Distinct from `image_watermark` (pure-Rust/image-only). `availability: beta` (Wave A — Lambda backend live; opt-in, MUST NOT 422; `short_form` is beta, `long_form` stays `planned` — no live Fargate worker; `multi_overlay_stack` stays `planned`). Per ticket [`4NrRPCgh`](https://trello.com/c/4NrRPCgh) + ADR-0013.
|
|
5488
6922
|
- video_text_watermark: Render a text overlay onto a base video via FFmpeg's `drawtext` filter. Single-input — text and styling in options. Same `watermark_mode` (single/tiled), anchor + margin vocab as `text_watermark`. Re-encode required; audio stream-copy passthrough. `availability: planned`; dispatch returns `feature_not_available` (422) until Lambda ships. Per ticket [`4NrRPCgh`](https://trello.com/c/4NrRPCgh) + ADR-0013.
|
|
5489
|
-
- split: Fan one input file into N outputs across GIF / PDF / audio / video MIME families. Single-input per-mime-group catalog (mirrors merge/convert): GIF uses `frame_range` (REQUIRED) + `output_format`; PDF uses `page_range` OR `page_groups` (mutually exclusive); audio + video use a `mode` discriminator (interval/count/cut_points) + numeric-seconds wire format + `precision` flag (fast/exact). 200-output hard cap per ADR-0009 §D5 with per-mode preflight math; output naming `output-001..output-200`. Long-form video routes to a separate `split-video-fargate` worker via `processing_class`. `availability:
|
|
6923
|
+
- split: Fan one input file into N outputs across GIF / PDF / audio / video MIME families. Single-input per-mime-group catalog (mirrors merge/convert): GIF uses `frame_range` (REQUIRED) + `output_format`; PDF uses `page_range` OR `page_groups` (mutually exclusive); audio + video use a `mode` discriminator (interval/count/cut_points) + numeric-seconds wire format + `precision` flag (fast/exact). 200-output hard cap per ADR-0009 §D5 with per-mode preflight math; output naming `output-001..output-200`. Long-form video routes to a separate `split-video-fargate` worker via `processing_class`. `availability: beta` for the `audio` and `video` mime_groups (workers live on staging — shape-stable + opt-in, MUST NOT 422); video activates short-form only (`video.processing_class.short_form: beta`; `long_form` / `split-video-fargate` stays `planned`). The `image_gif` and `document_pdf` mime_groups stay `availability: planned` and dispatch returns `feature_not_available` (422) until their workers ship. Per ticket [`vKI0CFDu`](https://trello.com/c/vKI0CFDu) + ADR-0014.
|
|
6924
|
+
- passthrough: Inert lossless source operation. A single-input source job whose SOLE operation is `passthrough` emits its source bytes UNCHANGED — no compression, no Lambda. The API self-completes the job at publish (terminal output = the upload `{bucket, key}` unchanged). Its purpose is to feed an uploaded file into a multi-input operation LOSSLESSLY: because `JobInputV2.source` is narrowed to exclude upload-direct, an upload that must enter a `merge` / `archive` / `image_watermark` op enters via a `passthrough` source job referenced downstream by `{type: job_output, from: <id>}` — preserving billing / DAG / lineage. Distinct from `operations: []` (which keeps its implicit-compress meaning on a single-input upload job); `passthrough` is the EXPLICIT lossless path via the "non-empty `operations[]` without `compress` = compression opt-out" rule. Media-agnostic; no options. `availability: beta` — activated (the inputs[]-narrowing + passthrough self-complete mechanism is deployed API-side); workflow-create accepts `passthrough` source jobs and MUST NOT return `feature_not_available`. **Never published to SNS** — deliberately absent from the AsyncAPI routing enums (API self-completes; no `ops-passthrough` queue). Per ticket [`4som89Uh`](https://trello.com/c/4som89Uh) + ADR-0004 (planned→beta flip).
|
|
5490
6925
|
|
|
5491
6926
|
Both the legacy `thumbnail` value and the four sub-type values
|
|
5492
6927
|
are valid routing targets today during the thumbnail migration
|
|
@@ -5505,6 +6940,7 @@ components:
|
|
|
5505
6940
|
- thumbnail_office
|
|
5506
6941
|
- image_watermark
|
|
5507
6942
|
- text_watermark
|
|
6943
|
+
- render_variants
|
|
5508
6944
|
- merge
|
|
5509
6945
|
- archive
|
|
5510
6946
|
- convert
|
|
@@ -5515,6 +6951,7 @@ components:
|
|
|
5515
6951
|
- video_watermark
|
|
5516
6952
|
- video_text_watermark
|
|
5517
6953
|
- split
|
|
6954
|
+
- passthrough
|
|
5518
6955
|
|
|
5519
6956
|
OperationInputModel:
|
|
5520
6957
|
type: string
|
|
@@ -5523,7 +6960,7 @@ components:
|
|
|
5523
6960
|
- single: One input file (compress, thumbnail, thumbnail_image,
|
|
5524
6961
|
thumbnail_video, thumbnail_document, thumbnail_office,
|
|
5525
6962
|
text_watermark, convert, audio_watermark,
|
|
5526
|
-
video_text_watermark, split)
|
|
6963
|
+
video_text_watermark, split, passthrough)
|
|
5527
6964
|
- multi: Multiple input files (merge, archive, image_watermark, custom_luma, audio_overlay, audio_to_video, video_watermark). audio_to_video is the first role-based op with an OPTIONAL role (min_inputs=1, max_inputs=2 — see `per_role_cardinality`); video_watermark mirrors `image_watermark`'s 2-required pattern on video bases.
|
|
5528
6965
|
enum:
|
|
5529
6966
|
- single
|
|
@@ -5691,6 +7128,7 @@ components:
|
|
|
5691
7128
|
|
|
5692
7129
|
UploadSuccessEnvelope:
|
|
5693
7130
|
type: object
|
|
7131
|
+
additionalProperties: false
|
|
5694
7132
|
required:
|
|
5695
7133
|
- success
|
|
5696
7134
|
- data
|
|
@@ -6303,6 +7741,7 @@ components:
|
|
|
6303
7741
|
|
|
6304
7742
|
MetadataSuccessEnvelope:
|
|
6305
7743
|
type: object
|
|
7744
|
+
additionalProperties: false
|
|
6306
7745
|
required:
|
|
6307
7746
|
- success
|
|
6308
7747
|
- data
|
|
@@ -6319,15 +7758,82 @@ components:
|
|
|
6319
7758
|
|
|
6320
7759
|
WorkflowCreateRequest:
|
|
6321
7760
|
type: object
|
|
6322
|
-
|
|
6323
|
-
-
|
|
7761
|
+
description: |
|
|
7762
|
+
Create a workflow. **Two mutually-exclusive request forms:**
|
|
7763
|
+
|
|
7764
|
+
- **Explicit jobs** — `jobs[]` (+ optional `workflow_edges`), the
|
|
7765
|
+
full multi-job DAG form. Use for multi-job workflows, role-based
|
|
7766
|
+
/ multi-input operations, output-as-role-input chains, explicit
|
|
7767
|
+
edges, or `delivery.selection.type: explicit`.
|
|
7768
|
+
- **Flat (single-job sugar)** — top-level `source` + `operations[]`,
|
|
7769
|
+
**exactly equivalent to `jobs: [{ source, operations }]`** (per
|
|
7770
|
+
ticket [`D0Gsri8V`](https://trello.com/c/D0Gsri8V)). The server
|
|
7771
|
+
lowers it into one `JobDefinition` and validates it as such —
|
|
7772
|
+
inheriting every `JobDefinition` guard verbatim — then runs the
|
|
7773
|
+
existing single-job canonicalizer. For the file-first
|
|
7774
|
+
dump-and-go case: one input + a set of single-input operations
|
|
7775
|
+
(a chain plus `base`-derived branches like thumbnail / split).
|
|
7776
|
+
|
|
7777
|
+
The two forms are mutually exclusive (a request supplies exactly
|
|
7778
|
+
one — enforced by the `oneOf` below); they are NOT combined.
|
|
7779
|
+
|
|
7780
|
+
Because the flat form lowers to ONE single-input job, its
|
|
7781
|
+
`operations[]` admits only **single-input** operation types. The
|
|
7782
|
+
multi-input / role operations (`merge`, `archive`,
|
|
7783
|
+
`image_watermark`, `custom_luma`, `audio_overlay`,
|
|
7784
|
+
`audio_to_video`, `video_watermark` — the enum at the
|
|
7785
|
+
`JobDefinition` multi-input guard) require `inputs[]` and MUST use
|
|
7786
|
+
the explicit `jobs[]` form; the server rejects them in flat mode
|
|
7787
|
+
via that same `JobDefinition` guard after lowering (not a
|
|
7788
|
+
separate structural rule). The flat form also does **not** support
|
|
7789
|
+
`workflow_edges` or `delivery.selection.type: explicit` — both
|
|
7790
|
+
need a named job id the single implicit job has none of; use
|
|
7791
|
+
explicit `jobs[]` for those. Delivery defaults and selection
|
|
7792
|
+
otherwise behave exactly as for one explicit job.
|
|
7793
|
+
oneOf:
|
|
7794
|
+
- title: Explicit jobs form
|
|
7795
|
+
required: [jobs]
|
|
7796
|
+
not:
|
|
7797
|
+
anyOf:
|
|
7798
|
+
- required: [source]
|
|
7799
|
+
- required: [operations]
|
|
7800
|
+
- title: Flat single-job form
|
|
7801
|
+
required: [source, operations]
|
|
7802
|
+
not:
|
|
7803
|
+
anyOf:
|
|
7804
|
+
- required: [jobs]
|
|
7805
|
+
- required: [workflow_edges]
|
|
6324
7806
|
properties:
|
|
6325
7807
|
jobs:
|
|
6326
7808
|
type: array
|
|
6327
|
-
description:
|
|
7809
|
+
description: |
|
|
7810
|
+
List of jobs in this workflow (the explicit-jobs form).
|
|
7811
|
+
Mutually exclusive with the flat `source` + `operations`
|
|
7812
|
+
form — supply exactly one.
|
|
6328
7813
|
minItems: 1
|
|
6329
7814
|
items:
|
|
6330
7815
|
$ref: '#/components/schemas/JobDefinition'
|
|
7816
|
+
source:
|
|
7817
|
+
$ref: '#/components/schemas/WorkflowSource'
|
|
7818
|
+
description: |
|
|
7819
|
+
Flat-form single input source. Present only in the flat form
|
|
7820
|
+
(with `operations`); equivalent to a single explicit job's
|
|
7821
|
+
`source`. Single-input `WorkflowSource` (4-leaf union incl.
|
|
7822
|
+
`upload`) — multi-input role-based jobs use explicit
|
|
7823
|
+
`jobs[].inputs[]`. See the schema description for the
|
|
7824
|
+
flat/explicit mutex.
|
|
7825
|
+
operations:
|
|
7826
|
+
type: array
|
|
7827
|
+
minItems: 1
|
|
7828
|
+
description: |
|
|
7829
|
+
Flat-form operation set (the unordered operations applied to
|
|
7830
|
+
the top-level `source`). Present only in the flat form (with
|
|
7831
|
+
`source`); equivalent to a single explicit job's
|
|
7832
|
+
`operations[]` and lowered + canonicalized identically.
|
|
7833
|
+
Single-input operation types only (see the schema
|
|
7834
|
+
description). Each entry is an `OperationDefinition`.
|
|
7835
|
+
items:
|
|
7836
|
+
$ref: '#/components/schemas/OperationDefinition'
|
|
6331
7837
|
workflow_edges:
|
|
6332
7838
|
type: array
|
|
6333
7839
|
description: |
|
|
@@ -6407,9 +7913,11 @@ components:
|
|
|
6407
7913
|
text_watermark, convert).
|
|
6408
7914
|
- `inputs[]` (multi-input): array of `JobInputV2` entries. Each entry
|
|
6409
7915
|
carries its own `source` plus an optional `role`
|
|
6410
|
-
(image_watermark, custom_luma, audio_overlay
|
|
7916
|
+
(image_watermark, custom_luma, audio_overlay, audio_to_video,
|
|
7917
|
+
video_watermark) and
|
|
6411
7918
|
`per_input_options`. Used by multi-input operations (merge,
|
|
6412
|
-
archive, image_watermark, custom_luma, audio_overlay
|
|
7919
|
+
archive, image_watermark, custom_luma, audio_overlay,
|
|
7920
|
+
audio_to_video, video_watermark).
|
|
6413
7921
|
|
|
6414
7922
|
Exactly one of `source` or `inputs` is required.
|
|
6415
7923
|
|
|
@@ -6472,8 +7980,12 @@ components:
|
|
|
6472
7980
|
type: array
|
|
6473
7981
|
description: |
|
|
6474
7982
|
Multi-input list for `merge`, `archive`, `image_watermark`,
|
|
6475
|
-
`custom_luma`, `audio_overlay`,
|
|
6476
|
-
|
|
7983
|
+
`custom_luma`, `audio_overlay`, `audio_to_video`, and
|
|
7984
|
+
`video_watermark`. Each
|
|
7985
|
+
entry is a `JobInputV2` with its own `MultiInputSource` (the
|
|
7986
|
+
3-leaf subset of `WorkflowSource` that excludes upload-direct;
|
|
7987
|
+
uploads enter via a `passthrough` source job referenced by
|
|
7988
|
+
`job_output`).
|
|
6477
7989
|
Mutually exclusive with `source` — the V2 shape boundary
|
|
6478
7990
|
stays `source` (single-input) XOR `inputs[]` (multi-input
|
|
6479
7991
|
role-based) per ADR-0004 / I12.
|
|
@@ -6492,14 +8004,27 @@ components:
|
|
|
6492
8004
|
operations:
|
|
6493
8005
|
type: array
|
|
6494
8006
|
description: |
|
|
6495
|
-
|
|
8007
|
+
Unordered **set** of operations for this job. The API
|
|
8008
|
+
canonicalizes them to a deterministic order — submitted order
|
|
8009
|
+
does not affect the result (same set, any order → same plan →
|
|
8010
|
+
same bytes). The resolved canonical order is echoed in the
|
|
8011
|
+
response `composition_plan` (see
|
|
8012
|
+
`WorkflowCreateResponse.composition_plan`). Each canonical chain
|
|
6496
8013
|
operation consumes the previous operation's output; all
|
|
6497
8014
|
intermediate and final outputs are kept.
|
|
6498
8015
|
|
|
8016
|
+
Order-independence is over a *well-formed* set: conflicting or
|
|
8017
|
+
duplicate same-stage operations (e.g. two `compress` with
|
|
8018
|
+
different options) are resolved or rejected by the
|
|
8019
|
+
canonicalization engine — the contract does not enumerate the
|
|
8020
|
+
conflict-resolution rules (engine-side, like the opaque
|
|
8021
|
+
processing-time estimator). A rejected set surfaces as a
|
|
8022
|
+
`validation_error` (422); it is not silently ordered.
|
|
8023
|
+
|
|
6499
8024
|
Multi-input jobs (with `inputs[]`) must have exactly one
|
|
6500
8025
|
operation, and it must be a multi-input type (`merge`,
|
|
6501
8026
|
`archive`, `image_watermark`, `custom_luma`,
|
|
6502
|
-
`audio_overlay`, or `
|
|
8027
|
+
`audio_overlay`, `audio_to_video`, or `video_watermark`).
|
|
6503
8028
|
items:
|
|
6504
8029
|
$ref: '#/components/schemas/OperationDefinition'
|
|
6505
8030
|
deliver:
|
|
@@ -6540,25 +8065,72 @@ components:
|
|
|
6540
8065
|
`custom_luma` (joined via ticket
|
|
6541
8066
|
[I29](https://trello.com/c/EPUE5Vs1) — `availability:
|
|
6542
8067
|
planned` so workflow-create returns 422 until Lambda
|
|
6543
|
-
ships),
|
|
8068
|
+
ships), `audio_overlay` (joined via ticket
|
|
6544
8069
|
[I19](https://trello.com/c/Xr3Z4GBF) — same `planned`
|
|
6545
|
-
gating)
|
|
6546
|
-
|
|
8070
|
+
gating), and `audio_to_video` + `video_watermark` (joined
|
|
8071
|
+
via Wave A [`c3uthIP4`](https://trello.com/c/c3uthIP4) —
|
|
8072
|
+
`availability: beta`, backends live). The multi-input
|
|
8073
|
+
shape is contract-defined for all entries.
|
|
8074
|
+
- if:
|
|
8075
|
+
properties:
|
|
8076
|
+
operations:
|
|
8077
|
+
contains:
|
|
8078
|
+
type: object
|
|
8079
|
+
properties:
|
|
8080
|
+
type:
|
|
8081
|
+
const: passthrough
|
|
8082
|
+
required: [type]
|
|
8083
|
+
required: [operations]
|
|
8084
|
+
then:
|
|
8085
|
+
properties:
|
|
8086
|
+
operations:
|
|
8087
|
+
minItems: 1
|
|
8088
|
+
maxItems: 1
|
|
8089
|
+
items:
|
|
8090
|
+
properties:
|
|
8091
|
+
type:
|
|
8092
|
+
const: passthrough
|
|
8093
|
+
source:
|
|
8094
|
+
properties:
|
|
8095
|
+
type:
|
|
8096
|
+
const: upload
|
|
8097
|
+
required: [type]
|
|
8098
|
+
required: [source]
|
|
8099
|
+
description: |
|
|
8100
|
+
`passthrough` constraint. A `passthrough` operation MUST be
|
|
8101
|
+
the SOLE operation of a SINGLE-INPUT job whose `source.type`
|
|
8102
|
+
is `upload` (i.e. `operations: [{type: passthrough}]` with
|
|
8103
|
+
`source: {type: upload, ...}`): never chained with another
|
|
8104
|
+
operation, never on `inputs[]`, never on a non-`upload`
|
|
8105
|
+
source. `passthrough` is an inert lossless bridge that the
|
|
8106
|
+
API self-completes at publish with the upload's `{bucket,
|
|
8107
|
+
key}` as the job output (see the `passthrough` bullet under
|
|
8108
|
+
`OperationType` and the `POST /api/workflows` description),
|
|
8109
|
+
so it is meaningful only for an uploaded source feeding a
|
|
8110
|
+
downstream multi-input op via `job_output`. The shared
|
|
8111
|
+
`OperationType` enum makes `passthrough` syntactically
|
|
8112
|
+
expressible elsewhere; this guard makes those uses
|
|
8113
|
+
schema-invalid rather than deferring rejection to the
|
|
8114
|
+
backend.
|
|
6547
8115
|
|
|
6548
8116
|
JobInputV2:
|
|
6549
8117
|
type: object
|
|
6550
8118
|
description: |
|
|
6551
8119
|
V2 multi-input entry per [ADR-0004](../docs/decisions/0004-job-shape.md)
|
|
6552
8120
|
§"JobInputV2". Replaces V1 `JobInput`. Each entry carries its own
|
|
6553
|
-
`source` (a `
|
|
6554
|
-
`
|
|
6555
|
-
external imports, vault connections, and upstream job
|
|
6556
|
-
within a single inputs[] array.
|
|
8121
|
+
`source` (a `MultiInputSource` value — the 3-leaf subset of
|
|
8122
|
+
`WorkflowSource` that **excludes upload-direct**), so multi-input
|
|
8123
|
+
jobs can mix external imports, vault connections, and upstream job
|
|
8124
|
+
outputs within a single inputs[] array. An uploaded file enters a
|
|
8125
|
+
multi-input op via a `passthrough` source job (single-input,
|
|
8126
|
+
`operations: [{type: passthrough}]`) referenced here as
|
|
8127
|
+
`{ type: job_output, from: <id> }` — not as a direct `upload`
|
|
8128
|
+
source (per ticket [`4som89Uh`](https://trello.com/c/4som89Uh)).
|
|
6557
8129
|
required:
|
|
6558
8130
|
- source
|
|
6559
8131
|
properties:
|
|
6560
8132
|
source:
|
|
6561
|
-
$ref: '#/components/schemas/
|
|
8133
|
+
$ref: '#/components/schemas/MultiInputSource'
|
|
6562
8134
|
role:
|
|
6563
8135
|
type: string
|
|
6564
8136
|
description: |
|
|
@@ -6632,6 +8204,24 @@ components:
|
|
|
6632
8204
|
rules. For example, `quality` is only valid when `mode: lossy` for
|
|
6633
8205
|
compress operations.
|
|
6634
8206
|
additionalProperties: true
|
|
8207
|
+
base:
|
|
8208
|
+
type: string
|
|
8209
|
+
enum:
|
|
8210
|
+
- processed_base
|
|
8211
|
+
- original
|
|
8212
|
+
default: processed_base
|
|
8213
|
+
description: |
|
|
8214
|
+
For **derived artifacts** (`thumbnail`, `split`): which canonical
|
|
8215
|
+
composition node this artifact branches from. Symbolic-only for
|
|
8216
|
+
v1 (no per-operation references — caller-visible op ids do not
|
|
8217
|
+
exist at submit time).
|
|
8218
|
+
- `processed_base` (default): the fully-processed base —
|
|
8219
|
+
post-everything, so the artifact carries the watermark + encode.
|
|
8220
|
+
- `original`: the untouched source file.
|
|
8221
|
+
Ignored for chain operations (compress, convert, watermark, merge,
|
|
8222
|
+
audio). The resolved branch point is echoed as
|
|
8223
|
+
`CompositionPlanOperation.derived_from` (a `node_id`) in
|
|
8224
|
+
`composition_plan`. See the operation-composition model.
|
|
6635
8225
|
|
|
6636
8226
|
# ============================================
|
|
6637
8227
|
# WORKFLOW DELIVERY (ticket I8-CONS)
|
|
@@ -6662,6 +8252,13 @@ components:
|
|
|
6662
8252
|
Optional operation type filter when the job has multiple
|
|
6663
8253
|
operations. Omit to select the last operation's output.
|
|
6664
8254
|
|
|
8255
|
+
**`availability: planned`** — per-operation-output grain is
|
|
8256
|
+
not yet honoured: the API currently rejects a non-empty
|
|
8257
|
+
`operation` filter with `feature_not_available` (422), even
|
|
8258
|
+
though `delivery.selection` `all_outputs`/`explicit` are
|
|
8259
|
+
`stable` at JOB grain (per `cse3wt2s` / T5b). Deferred to a
|
|
8260
|
+
follow-up; omit it (job-grain selection) for now.
|
|
8261
|
+
|
|
6665
8262
|
DeliverySelection:
|
|
6666
8263
|
type: object
|
|
6667
8264
|
description: |
|
|
@@ -6677,8 +8274,10 @@ components:
|
|
|
6677
8274
|
Selection strategy:
|
|
6678
8275
|
- `terminal` (default): bundle all jobs with no outgoing
|
|
6679
8276
|
edges (computed from `JobOutputSource.from` references).
|
|
6680
|
-
- `all_outputs`: bundle every
|
|
6681
|
-
workflow, including intermediates.
|
|
8277
|
+
- `all_outputs`: bundle every **job's** output in the
|
|
8278
|
+
workflow, including intermediates (job-grain). Per-operation
|
|
8279
|
+
output selection within a job is NOT yet available — see the
|
|
8280
|
+
`DeliveryOutputRef.operation` filter (`planned`).
|
|
6682
8281
|
- `explicit`: bundle only the refs listed in `refs[]`.
|
|
6683
8282
|
Mutually exclusive with per-job `JobDefinition.deliver: true`
|
|
6684
8283
|
(validator rejects 422 if both are present).
|
|
@@ -6688,19 +8287,21 @@ components:
|
|
|
6688
8287
|
- explicit
|
|
6689
8288
|
per_value_availability:
|
|
6690
8289
|
terminal: { availability: stable }
|
|
6691
|
-
all_outputs: { availability:
|
|
6692
|
-
explicit: { availability:
|
|
6693
|
-
# Availability flipped
|
|
6694
|
-
# [`
|
|
6695
|
-
#
|
|
6696
|
-
#
|
|
6697
|
-
#
|
|
6698
|
-
#
|
|
6699
|
-
# threads the request-level
|
|
6700
|
-
# flips back
|
|
6701
|
-
#
|
|
6702
|
-
#
|
|
6703
|
-
#
|
|
8290
|
+
all_outputs: { availability: stable }
|
|
8291
|
+
explicit: { availability: stable }
|
|
8292
|
+
# Availability flipped planned → stable per ticket
|
|
8293
|
+
# [`cse3wt2s`](https://trello.com/c/cse3wt2s) (T5b delivery-
|
|
8294
|
+
# selection go-live, co-land with API PR #365). The two-gate
|
|
8295
|
+
# flip: co0CERtJ downgraded these to `planned` to match the
|
|
8296
|
+
# runtime, which 422'd every non-default `selection.type`; the
|
|
8297
|
+
# API now ACCEPTS `all_outputs`/`explicit` at JOB grain
|
|
8298
|
+
# (`DeliveryPlanComputer` threads the request-level
|
|
8299
|
+
# `delivery.selection`), so the contract flips back to `stable`.
|
|
8300
|
+
# The `all_outputs` claim is narrowed to job-grain to stay
|
|
8301
|
+
# conformant; per-operation-output grain + the
|
|
8302
|
+
# `DeliveryOutputRef.operation` filter stay `planned` (the API
|
|
8303
|
+
# still 422s the operation filter — deferred follow-up). Terminal
|
|
8304
|
+
# stays `stable` (the implicit default).
|
|
6704
8305
|
refs:
|
|
6705
8306
|
type: array
|
|
6706
8307
|
description: |
|
|
@@ -6828,6 +8429,18 @@ components:
|
|
|
6828
8429
|
description: |
|
|
6829
8430
|
Set when `reason: consumed_by`. The id of the downstream
|
|
6830
8431
|
job that consumes this output.
|
|
8432
|
+
node_id:
|
|
8433
|
+
type: string
|
|
8434
|
+
description: |
|
|
8435
|
+
Symbolic composition `node_id` correlating this delivered output
|
|
8436
|
+
to its canonical node in `composition_plan` (e.g. `encode`,
|
|
8437
|
+
`thumbnail`, `processed_base`). Lets consumers label and group
|
|
8438
|
+
delivered files by composition role. **Optional** — emitted once
|
|
8439
|
+
the canonicalization engine is live (optional-then-promote,
|
|
8440
|
+
mirroring `composition_plan`); absent until then. This is the
|
|
8441
|
+
additive carrier; the normative
|
|
8442
|
+
`/downloads == delivery_plan.outputs[]` rendezvous invariant is
|
|
8443
|
+
introduced with the delivery-selection promotion, not here.
|
|
6831
8444
|
|
|
6832
8445
|
DeliveryPlan:
|
|
6833
8446
|
type: object
|
|
@@ -7086,6 +8699,205 @@ components:
|
|
|
7086
8699
|
class_hint:
|
|
7087
8700
|
$ref: '#/components/schemas/ProcessingClassHint'
|
|
7088
8701
|
|
|
8702
|
+
# ============================================
|
|
8703
|
+
# CANONICAL COMPOSITION PLAN (operation-composition epic)
|
|
8704
|
+
# ============================================
|
|
8705
|
+
# Response-side single source of truth for operation composition.
|
|
8706
|
+
# The API accepts an unordered operation SET (per source) and
|
|
8707
|
+
# canonicalizes it to a deterministic DAG; this plan exposes the
|
|
8708
|
+
# resolved order, per-op chain group/position, derived-artifact
|
|
8709
|
+
# lineage, and image-encode capabilities so FE + SDK read the model
|
|
8710
|
+
# instead of hardcoding it. Additive-first: these schemas ship OPEN
|
|
8711
|
+
# (no `additionalProperties: false`, no `allOf`) — flat objects to
|
|
8712
|
+
# avoid the inherited-field-closure footgun, and OPEN so the
|
|
8713
|
+
# canonicalization engine (later epic phases) can add correlation
|
|
8714
|
+
# fields before a separate tightening cut. All correlation fields
|
|
8715
|
+
# (`node_id`, `operation_id`, `requested_operations`, capability
|
|
8716
|
+
# keys) are pinned now so the eventual `additionalProperties: false`
|
|
8717
|
+
# cut has nothing to break.
|
|
8718
|
+
|
|
8719
|
+
CompositionPlan:
|
|
8720
|
+
type: object
|
|
8721
|
+
description: |
|
|
8722
|
+
Canonical composition plan for a workflow — emitted on
|
|
8723
|
+
`WorkflowCreateResponse.composition_plan`. Tells callers the
|
|
8724
|
+
deterministic order the submitted operation set resolved to, the
|
|
8725
|
+
per-operation chain group/position, derived-artifact lineage, and
|
|
8726
|
+
the image-encode capabilities that gate format/quality choices.
|
|
8727
|
+
|
|
8728
|
+
`canonical_order` is the informational pipeline spine; per-op
|
|
8729
|
+
detail lives on each `CompositionPlanOperation`.
|
|
8730
|
+
required:
|
|
8731
|
+
- canonical_order
|
|
8732
|
+
- jobs
|
|
8733
|
+
- capabilities
|
|
8734
|
+
properties:
|
|
8735
|
+
canonical_order:
|
|
8736
|
+
type: array
|
|
8737
|
+
description: |
|
|
8738
|
+
The canonical pipeline spine (single mutated stream), as an
|
|
8739
|
+
ordered list of `chain_group` stage names. The known image/AV
|
|
8740
|
+
spine is `merge` (fan-in) → `image_watermark` → `text_watermark`
|
|
8741
|
+
→ `audio` → `encode` (terminal: convert+compress folded) →
|
|
8742
|
+
`derived` (fan-out: thumbnail/split). Informational — read per-op
|
|
8743
|
+
`chain_group` / `chain_position` for placement. The geometry
|
|
8744
|
+
stage is intentionally not exposed as a node for v1 (image
|
|
8745
|
+
resize/fit live inside the thumbnail node).
|
|
8746
|
+
|
|
8747
|
+
**Open string, NOT a fixed enum** (mirrors
|
|
8748
|
+
`ProcessingPlanJob.execution_pool`): the canonical model still
|
|
8749
|
+
covers operation classes not in the image-pipeline spine
|
|
8750
|
+
(`archive`, `passthrough`, `audio_to_video`, …) and the engine's
|
|
8751
|
+
final stage taxonomy is pinned alongside the canonicalization
|
|
8752
|
+
engine (later epic phases). The values are tightened to an enum
|
|
8753
|
+
in the same gated cut that adds `additionalProperties: false`,
|
|
8754
|
+
once the engine emits the complete set. Consumers MUST treat
|
|
8755
|
+
unknown stage names as forward-compatible.
|
|
8756
|
+
items:
|
|
8757
|
+
type: string
|
|
8758
|
+
jobs:
|
|
8759
|
+
type: array
|
|
8760
|
+
description: Per-job canonical composition. Empty `[]` is permitted.
|
|
8761
|
+
items:
|
|
8762
|
+
$ref: '#/components/schemas/CompositionPlanJob'
|
|
8763
|
+
capabilities:
|
|
8764
|
+
$ref: '#/components/schemas/ImageEncodeCapabilities'
|
|
8765
|
+
|
|
8766
|
+
CompositionPlanJob:
|
|
8767
|
+
type: object
|
|
8768
|
+
description: Per-job entry in a `CompositionPlan` — the canonical operations for one job.
|
|
8769
|
+
required:
|
|
8770
|
+
- job_id
|
|
8771
|
+
- operations
|
|
8772
|
+
properties:
|
|
8773
|
+
job_id:
|
|
8774
|
+
$ref: '#/components/schemas/UuidV7'
|
|
8775
|
+
operations:
|
|
8776
|
+
type: array
|
|
8777
|
+
description: The job's operations in canonical (resolved) order.
|
|
8778
|
+
items:
|
|
8779
|
+
$ref: '#/components/schemas/CompositionPlanOperation'
|
|
8780
|
+
|
|
8781
|
+
CompositionPlanOperation:
|
|
8782
|
+
type: object
|
|
8783
|
+
description: |
|
|
8784
|
+
The canonical view of one operation within a job — its symbolic
|
|
8785
|
+
composition node, resolved chain placement, and (for derived
|
|
8786
|
+
artifacts) the node it branches from.
|
|
8787
|
+
required:
|
|
8788
|
+
- node_id
|
|
8789
|
+
- type
|
|
8790
|
+
- chain_group
|
|
8791
|
+
- chain_position
|
|
8792
|
+
properties:
|
|
8793
|
+
node_id:
|
|
8794
|
+
type: string
|
|
8795
|
+
description: |
|
|
8796
|
+
Stable **symbolic** canonical node id (e.g. `original`,
|
|
8797
|
+
`processed_base`, `encode`, `thumbnail`). The correlation key
|
|
8798
|
+
used by `derived_from`, `DeliveryPlanOutput.node_id`, and
|
|
8799
|
+
`OperationDownload.node_id` to tie delivered files back to a
|
|
8800
|
+
canonical node. **NOT** the operation UUID. (SSE stays
|
|
8801
|
+
per-operation by design — it is not a delivery-plan projection
|
|
8802
|
+
and does not carry `node_id`.)
|
|
8803
|
+
example: encode
|
|
8804
|
+
operation_id:
|
|
8805
|
+
$ref: '#/components/schemas/UuidV7'
|
|
8806
|
+
type:
|
|
8807
|
+
$ref: '#/components/schemas/OperationType'
|
|
8808
|
+
description: |
|
|
8809
|
+
The canonical operation type of this node. For the folded
|
|
8810
|
+
terminal `encode` node (`chain_group: encode`) this is the
|
|
8811
|
+
encode operation `compress` — `convert` folds INTO it (a single
|
|
8812
|
+
terminal encode, never a double-encode), and the folded source
|
|
8813
|
+
operations are listed in `requested_operations`. For all other
|
|
8814
|
+
nodes `type` is the operation as submitted.
|
|
8815
|
+
chain_group:
|
|
8816
|
+
type: string
|
|
8817
|
+
description: |
|
|
8818
|
+
Which canonical stage this operation belongs to. **Open string,
|
|
8819
|
+
NOT a fixed enum** (mirrors `ProcessingPlanJob.execution_pool`) —
|
|
8820
|
+
the stage taxonomy is pinned alongside the canonicalization
|
|
8821
|
+
engine and tightened to an enum in the same gated cut that adds
|
|
8822
|
+
`additionalProperties: false`; consumers MUST treat unknown
|
|
8823
|
+
stage names as forward-compatible. The watermark stages are
|
|
8824
|
+
**media-agnostic overlay stages**, not image-only. Known stages:
|
|
8825
|
+
- `merge`: fan-in.
|
|
8826
|
+
- `image_watermark`: raster/image-overlay watermark stage —
|
|
8827
|
+
covers the `image_watermark` AND `video_watermark` operation
|
|
8828
|
+
types.
|
|
8829
|
+
- `text_watermark`: text-overlay watermark stage — covers the
|
|
8830
|
+
`text_watermark` AND `video_text_watermark` operation types.
|
|
8831
|
+
- `audio`: audio operations (e.g. `audio_overlay`,
|
|
8832
|
+
`audio_to_video`).
|
|
8833
|
+
- `encode`: the folded convert+compress terminal encode stage.
|
|
8834
|
+
- `derived`: fan-out artifact (`thumbnail` / `split`).
|
|
8835
|
+
Operation classes outside the image/AV spine (e.g. `archive`
|
|
8836
|
+
bundling, `passthrough` bridge jobs) carry their own stage names
|
|
8837
|
+
once the engine pins them. (The geometry/resize stage is not
|
|
8838
|
+
exposed as a node for v1 — image resize lives inside the
|
|
8839
|
+
`thumbnail` worker.)
|
|
8840
|
+
chain_position:
|
|
8841
|
+
type: integer
|
|
8842
|
+
minimum: 0
|
|
8843
|
+
description: Deterministic resolved position within the canonical chain.
|
|
8844
|
+
derived_from:
|
|
8845
|
+
type: string
|
|
8846
|
+
description: |
|
|
8847
|
+
Set only for derived operations (`thumbnail`, `split`): the
|
|
8848
|
+
`node_id` this artifact branches from (symbolic; resolved from
|
|
8849
|
+
the request-side `OperationDefinition.base`). Absent for chain
|
|
8850
|
+
operations.
|
|
8851
|
+
example: processed_base
|
|
8852
|
+
requested_operations:
|
|
8853
|
+
type: array
|
|
8854
|
+
description: |
|
|
8855
|
+
Image-fold audit trail: the request operation types that were
|
|
8856
|
+
absorbed into this single canonical node. For the folded
|
|
8857
|
+
`encode` node this is e.g. `[convert, compress]` — the caller
|
|
8858
|
+
still submits both, but composition exposes ONE `encode` node
|
|
8859
|
+
(no false double-encode). Absent when no fold occurred.
|
|
8860
|
+
items:
|
|
8861
|
+
$ref: '#/components/schemas/OperationType'
|
|
8862
|
+
|
|
8863
|
+
ImageEncodeCapabilities:
|
|
8864
|
+
type: object
|
|
8865
|
+
description: |
|
|
8866
|
+
Image-encode-stage capability flags that gate format/quality
|
|
8867
|
+
choices for the canonical `encode` node. **Image-encode-scoped**
|
|
8868
|
+
— these caveats apply to the image encode pass only, not to
|
|
8869
|
+
video / audio / document encoding. Surfaced so FE/SDK do not
|
|
8870
|
+
promise capabilities the worker cannot honour (e.g. a WebP
|
|
8871
|
+
quality slider the convert path silently ignores).
|
|
8872
|
+
required:
|
|
8873
|
+
- webp_quality_supported
|
|
8874
|
+
- background_flatten
|
|
8875
|
+
properties:
|
|
8876
|
+
webp_quality_supported:
|
|
8877
|
+
type: boolean
|
|
8878
|
+
description: |
|
|
8879
|
+
Whether quality-controlled (lossy) WebP encode is available.
|
|
8880
|
+
`false` today: WebP via the convert/encode path is
|
|
8881
|
+
lossless-only and the `quality` option is silently ignored
|
|
8882
|
+
(quality-controlled WebP is only reachable via `compress`).
|
|
8883
|
+
Consumers MUST NOT offer a WebP quality control when this is
|
|
8884
|
+
`false`.
|
|
8885
|
+
example: false
|
|
8886
|
+
background_flatten:
|
|
8887
|
+
type: string
|
|
8888
|
+
description: |
|
|
8889
|
+
Whether alpha→non-alpha background flattening is available at
|
|
8890
|
+
encode time. `conditional` today: flatten fires only when an
|
|
8891
|
+
alpha source is encoded to a non-alpha output (e.g. PNG→JPEG).
|
|
8892
|
+
- `unsupported`: never applied.
|
|
8893
|
+
- `conditional`: applied only on alpha→non-alpha transitions.
|
|
8894
|
+
- `supported`: always available.
|
|
8895
|
+
enum:
|
|
8896
|
+
- unsupported
|
|
8897
|
+
- conditional
|
|
8898
|
+
- supported
|
|
8899
|
+
example: conditional
|
|
8900
|
+
|
|
7089
8901
|
# ============================================
|
|
7090
8902
|
# UPLOAD PREFLIGHT PROBE (per ticket I28)
|
|
7091
8903
|
# ============================================
|
|
@@ -7291,6 +9103,7 @@ components:
|
|
|
7291
9103
|
|
|
7292
9104
|
UploadProbeSuccessEnvelope:
|
|
7293
9105
|
type: object
|
|
9106
|
+
additionalProperties: false
|
|
7294
9107
|
description: |
|
|
7295
9108
|
Success envelope wrapping `UploadProbeResponse` on
|
|
7296
9109
|
`POST /api/uploads/{id}/probe` 200 responses, per ticket
|
|
@@ -7478,6 +9291,24 @@ components:
|
|
|
7478
9291
|
frontend can preview routing decisions before the
|
|
7479
9292
|
workflow runs. Estimation algorithm is server-side and
|
|
7480
9293
|
deliberately opaque per plan v5 §F8.2.
|
|
9294
|
+
composition_plan:
|
|
9295
|
+
$ref: '#/components/schemas/CompositionPlan'
|
|
9296
|
+
description: |
|
|
9297
|
+
Server-computed canonical composition plan — the single
|
|
9298
|
+
source of truth for how the submitted operation **set** was
|
|
9299
|
+
canonicalized into a deterministic DAG (canonical order,
|
|
9300
|
+
per-op chain group/position, derived-artifact lineage, and
|
|
9301
|
+
image-encode capabilities). Frontend + SDK read this instead
|
|
9302
|
+
of hardcoding the pipeline order (which today lives only in
|
|
9303
|
+
the API), so click-order never changes the result.
|
|
9304
|
+
|
|
9305
|
+
**OPTIONAL (optional-then-promote).** The canonicalization
|
|
9306
|
+
engine that emits it ships in later phases of the
|
|
9307
|
+
operation-composition epic; until it is live the server omits
|
|
9308
|
+
this field rather than emitting a partial plan. Promoted to
|
|
9309
|
+
required once the engine emits reliably (the same
|
|
9310
|
+
optional-then-promote path `delivery_plan` / `processing_plan`
|
|
9311
|
+
took). Consumers MUST treat it as may-be-absent.
|
|
7481
9312
|
warnings:
|
|
7482
9313
|
type: array
|
|
7483
9314
|
description: |
|
|
@@ -7515,6 +9346,7 @@ components:
|
|
|
7515
9346
|
|
|
7516
9347
|
WorkflowCreateSuccessEnvelope:
|
|
7517
9348
|
type: object
|
|
9349
|
+
additionalProperties: false
|
|
7518
9350
|
required:
|
|
7519
9351
|
- success
|
|
7520
9352
|
- data
|
|
@@ -7566,9 +9398,25 @@ components:
|
|
|
7566
9398
|
(per ticket [I24](https://trello.com/c/e50uXLcl)). Carries
|
|
7567
9399
|
`paused_at`, `expires_at`, `required_action`, and
|
|
7568
9400
|
actionable `links` (resume / top_up / upgrade).
|
|
9401
|
+
composition_plan:
|
|
9402
|
+
$ref: '#/components/schemas/CompositionPlan'
|
|
9403
|
+
description: |
|
|
9404
|
+
Server-computed canonical composition plan, echoed on the
|
|
9405
|
+
status read so a frontend reload / resume can re-derive the
|
|
9406
|
+
canonicalized DAG (canonical order, per-op chain
|
|
9407
|
+
group/position, derived-artifact lineage, image-encode
|
|
9408
|
+
capabilities) without re-submitting. Identical shape and
|
|
9409
|
+
semantics to `WorkflowCreateResponse.composition_plan`.
|
|
9410
|
+
|
|
9411
|
+
**OPTIONAL (optional-then-promote).** The canonicalization
|
|
9412
|
+
engine that emits it ships in later phases of the
|
|
9413
|
+
operation-composition epic; the server omits the field until
|
|
9414
|
+
it is live (mirroring the create side). Per ticket
|
|
9415
|
+
[`RrxdUBGZ`](https://trello.com/c/RrxdUBGZ) / `i2J8AMy6`.
|
|
7569
9416
|
|
|
7570
9417
|
WorkflowStatusSuccessEnvelope:
|
|
7571
9418
|
type: object
|
|
9419
|
+
additionalProperties: false
|
|
7572
9420
|
required:
|
|
7573
9421
|
- success
|
|
7574
9422
|
- data
|
|
@@ -7579,6 +9427,118 @@ components:
|
|
|
7579
9427
|
data:
|
|
7580
9428
|
$ref: '#/components/schemas/WorkflowStatusResponse'
|
|
7581
9429
|
|
|
9430
|
+
JobMediaClass:
|
|
9431
|
+
type: string
|
|
9432
|
+
description: |
|
|
9433
|
+
The media class of a job (the kind of media it processes), distinct
|
|
9434
|
+
from an operation's step `type` (`convert` / `compress` / …). `mixed`
|
|
9435
|
+
is a multi-input job spanning more than one class (e.g. an archive or
|
|
9436
|
+
a merge of heterogeneous inputs). Used in the lightweight workflows
|
|
9437
|
+
list row so a UI can label/group rows by media without drilling into
|
|
9438
|
+
per-operation detail.
|
|
9439
|
+
enum:
|
|
9440
|
+
- image
|
|
9441
|
+
- video
|
|
9442
|
+
- audio
|
|
9443
|
+
- document
|
|
9444
|
+
- mixed
|
|
9445
|
+
|
|
9446
|
+
WorkflowSummaryJob:
|
|
9447
|
+
type: object
|
|
9448
|
+
description: |
|
|
9449
|
+
Minimal per-job entry in a `WorkflowSummary` row — `job_type` (media
|
|
9450
|
+
class) + status for list rendering. Per-operation detail (op step
|
|
9451
|
+
types, `result_metadata`) is the drill-in
|
|
9452
|
+
`GET /api/workflows/{id}/status`; outputs are
|
|
9453
|
+
`GET /api/workflows/{id}/downloads`.
|
|
9454
|
+
required:
|
|
9455
|
+
- job_id
|
|
9456
|
+
- ref
|
|
9457
|
+
- job_type
|
|
9458
|
+
- status
|
|
9459
|
+
properties:
|
|
9460
|
+
job_id:
|
|
9461
|
+
$ref: '#/components/schemas/UuidV7'
|
|
9462
|
+
ref:
|
|
9463
|
+
type: string
|
|
9464
|
+
description: Workflow-local job ref (matches `JobResponse.ref`).
|
|
9465
|
+
job_type:
|
|
9466
|
+
$ref: '#/components/schemas/JobMediaClass'
|
|
9467
|
+
status:
|
|
9468
|
+
$ref: '#/components/schemas/JobStatus'
|
|
9469
|
+
|
|
9470
|
+
WorkflowSummary:
|
|
9471
|
+
type: object
|
|
9472
|
+
description: |
|
|
9473
|
+
Lightweight summary row for `GET /api/workflows` (list). Carries
|
|
9474
|
+
just enough for a list view — id / status / created_at + per-job
|
|
9475
|
+
`{ ref, job_type (media class), status }` — and deliberately does
|
|
9476
|
+
NOT inline per-operation detail, `result_metadata`, output counts,
|
|
9477
|
+
or download URLs (those are the drill-in `GET /{id}/status` +
|
|
9478
|
+
`GET /{id}/downloads` reads; keeping rows lean avoids N×M×K payload
|
|
9479
|
+
blow-up). Field naming mirrors `WorkflowStatusResponse`
|
|
9480
|
+
(`workflow_id`, snake_case).
|
|
9481
|
+
required:
|
|
9482
|
+
- workflow_id
|
|
9483
|
+
- status
|
|
9484
|
+
- created_at
|
|
9485
|
+
- jobs
|
|
9486
|
+
properties:
|
|
9487
|
+
workflow_id:
|
|
9488
|
+
$ref: '#/components/schemas/UuidV7'
|
|
9489
|
+
status:
|
|
9490
|
+
$ref: '#/components/schemas/WorkflowStatus'
|
|
9491
|
+
created_at:
|
|
9492
|
+
type: string
|
|
9493
|
+
format: date-time
|
|
9494
|
+
description: ISO-8601 workflow-creation timestamp (the list sort key, DESC).
|
|
9495
|
+
jobs:
|
|
9496
|
+
type: array
|
|
9497
|
+
items:
|
|
9498
|
+
$ref: '#/components/schemas/WorkflowSummaryJob'
|
|
9499
|
+
|
|
9500
|
+
WorkflowListResponse:
|
|
9501
|
+
type: object
|
|
9502
|
+
description: |
|
|
9503
|
+
Cursor-paginated page of workflow summaries (most-recent-first).
|
|
9504
|
+
Walk pages by passing `next_cursor` as the next request's `cursor`
|
|
9505
|
+
query parameter until `is_truncated: false`. Cursor pagination
|
|
9506
|
+
(opaque `(created_at, id)` token) mirrors the multipart-parts
|
|
9507
|
+
listing convention — NOT the offset-based `/api/v2/credits/usage`.
|
|
9508
|
+
required:
|
|
9509
|
+
- workflows
|
|
9510
|
+
- is_truncated
|
|
9511
|
+
properties:
|
|
9512
|
+
workflows:
|
|
9513
|
+
type: array
|
|
9514
|
+
description: This page of workflow summary rows (most-recent-first).
|
|
9515
|
+
items:
|
|
9516
|
+
$ref: '#/components/schemas/WorkflowSummary'
|
|
9517
|
+
next_cursor:
|
|
9518
|
+
description: |
|
|
9519
|
+
Opaque cursor — pass as the next request's `cursor` query
|
|
9520
|
+
parameter to fetch the following page. `null` on the final page
|
|
9521
|
+
(or when `is_truncated: false`). Treat as opaque; do not parse.
|
|
9522
|
+
oneOf:
|
|
9523
|
+
- type: string
|
|
9524
|
+
- type: 'null'
|
|
9525
|
+
is_truncated:
|
|
9526
|
+
type: boolean
|
|
9527
|
+
description: '`true` when more pages remain; `false` on the final page.'
|
|
9528
|
+
|
|
9529
|
+
WorkflowListSuccessEnvelope:
|
|
9530
|
+
type: object
|
|
9531
|
+
additionalProperties: false
|
|
9532
|
+
required:
|
|
9533
|
+
- success
|
|
9534
|
+
- data
|
|
9535
|
+
properties:
|
|
9536
|
+
success:
|
|
9537
|
+
type: boolean
|
|
9538
|
+
enum: [true]
|
|
9539
|
+
data:
|
|
9540
|
+
$ref: '#/components/schemas/WorkflowListResponse'
|
|
9541
|
+
|
|
7582
9542
|
JobResponse:
|
|
7583
9543
|
type: object
|
|
7584
9544
|
description: Job status within a workflow response
|
|
@@ -7597,6 +9557,24 @@ components:
|
|
|
7597
9557
|
$ref: '#/components/schemas/UuidV7'
|
|
7598
9558
|
status:
|
|
7599
9559
|
$ref: '#/components/schemas/JobStatus'
|
|
9560
|
+
processing_class:
|
|
9561
|
+
$ref: '#/components/schemas/ProcessingClass'
|
|
9562
|
+
description: |
|
|
9563
|
+
The logical processing class the server resolved this job to.
|
|
9564
|
+
The status-poll echo of `WorkflowCreateResponse.processing_plan`
|
|
9565
|
+
`.jobs[].processing_class` (`ProcessingPlanJob`) — same enum, same
|
|
9566
|
+
per-job granularity — so consumers can observe the resolved
|
|
9567
|
+
routing decision (`short_form` vs `long_form`, plus the merge-only
|
|
9568
|
+
`*_concat` / `*_re_encode` aliases) after create, not just at
|
|
9569
|
+
create time. The server always knows a job's class once the
|
|
9570
|
+
workflow is resolved and populates this on every status response
|
|
9571
|
+
(mirrors the `ProcessingPlanJob` invariant); it is left
|
|
9572
|
+
schema-OPTIONAL rather than `required` only because `JobResponse`
|
|
9573
|
+
is also embedded in the `WebhookPayload.workflow` callback payload,
|
|
9574
|
+
where a required addition would be a breaking request-contract
|
|
9575
|
+
change for existing webhook consumers. Per ticket
|
|
9576
|
+
[F3dL0UKz](https://trello.com/c/F3dL0UKz); consumed by api
|
|
9577
|
+
[D3yN1SGm](https://trello.com/c/D3yN1SGm).
|
|
7600
9578
|
depends_on:
|
|
7601
9579
|
type: array
|
|
7602
9580
|
description: List of upstream job refs this job depends on
|
|
@@ -7628,6 +9606,18 @@ components:
|
|
|
7628
9606
|
description: Progress percentage (0-100). Present when in_progress or completed.
|
|
7629
9607
|
result:
|
|
7630
9608
|
$ref: '#/components/schemas/OperationResult'
|
|
9609
|
+
result_metadata:
|
|
9610
|
+
$ref: '#/components/schemas/OperationResultMetadata'
|
|
9611
|
+
description: |
|
|
9612
|
+
Whitelisted operation-level metadata (the read projection — what
|
|
9613
|
+
`GET /api/workflows/{id}/status` emits per operation). Optional;
|
|
9614
|
+
present once an operation produces a whitelisted key. Distinct
|
|
9615
|
+
from `result` (the deliverable output): this carries small
|
|
9616
|
+
per-operation metadata, not the file. The `/status` read carries
|
|
9617
|
+
ONLY this `result_metadata` for op-level detail — the output
|
|
9618
|
+
`result` (`download_url`/`size_bytes`) is read via
|
|
9619
|
+
`GET /api/workflows/{id}/downloads` per the rmP7ndhK/ZjVXHkYK
|
|
9620
|
+
narrowing. Per ticket `dw048NKk` (A2) / `EurbZLMH` (B1).
|
|
7631
9621
|
error_code:
|
|
7632
9622
|
type: string
|
|
7633
9623
|
description: |
|
|
@@ -7644,6 +9634,32 @@ components:
|
|
|
7644
9634
|
absent otherwise. Mirrors `SseOperationFailedData.error_message`.
|
|
7645
9635
|
example: "output_too_large: Output (12156489 bytes) is not smaller than input (6187609 bytes)"
|
|
7646
9636
|
|
|
9637
|
+
OperationResultMetadata:
|
|
9638
|
+
type: object
|
|
9639
|
+
additionalProperties: false
|
|
9640
|
+
description: |
|
|
9641
|
+
Whitelisted, named operation-level result metadata — a **CLOSED**
|
|
9642
|
+
set of declared keys. The API serves ONLY these declared keys (a
|
|
9643
|
+
whitelist projection), never the raw stored metadata bag, so internal
|
|
9644
|
+
diagnostics never leak. New keys are **additive named cuts** (the
|
|
9645
|
+
`additionalProperties: false` closure is the point — an unmodelled
|
|
9646
|
+
key is a coordinated contract change, not a silent rollout). Twin of
|
|
9647
|
+
the AsyncAPI `OperationResultMetadata` (wire ↔ read parity). Distinct
|
|
9648
|
+
from `OperationResult` (the deliverable output file): this carries
|
|
9649
|
+
small per-operation metadata, not the output. Per `EurbZLMH` (B1).
|
|
9650
|
+
properties:
|
|
9651
|
+
watermark_id:
|
|
9652
|
+
type: string
|
|
9653
|
+
format: uuid
|
|
9654
|
+
description: |
|
|
9655
|
+
UUIDv7 of the watermark embedded by an `audio_watermark`
|
|
9656
|
+
operation (for later decode/verify). **Display-only / not yet
|
|
9657
|
+
populated** — gated on the audio_watermark Lambda (`EvPl5fkH`,
|
|
9658
|
+
B3) + the watermark registry (B2, deferred). The field +
|
|
9659
|
+
whitelist ship now (additive) so the result_metadata pipeline +
|
|
9660
|
+
read projection land without a later contract bump; absent in
|
|
9661
|
+
practice until B3 is live.
|
|
9662
|
+
|
|
7647
9663
|
OperationResult:
|
|
7648
9664
|
type: object
|
|
7649
9665
|
description: |
|
|
@@ -7665,7 +9681,20 @@ components:
|
|
|
7665
9681
|
description: Key in the customer's export destination (if export configured)
|
|
7666
9682
|
metrics:
|
|
7667
9683
|
type: object
|
|
7668
|
-
description:
|
|
9684
|
+
description: |
|
|
9685
|
+
Operation-specific performance metrics, carried on the
|
|
9686
|
+
**completion result** surface (SSE `operation.completed`
|
|
9687
|
+
result / result-on-poll), NOT on the workflow **status**
|
|
9688
|
+
read (confirmed ZjVXHkYK / api). `GET /api/workflows/{id}`
|
|
9689
|
+
serialises each operation as `{id, type, status}` plus a
|
|
9690
|
+
conditional `error_message`/`error_code` — it does NOT carry
|
|
9691
|
+
`result.metrics`, so `re_encode_decision` / `re_encode_reason`
|
|
9692
|
+
are not readable from the status poll. Canonical reads for
|
|
9693
|
+
those: the per-job `processing_class` echo (on the status
|
|
9694
|
+
response) for the route, and `GET /api/workflows/{id}/downloads`
|
|
9695
|
+
(per-output `size_bytes`) for the authoritative output size.
|
|
9696
|
+
Note `metrics` has no `output_size_bytes` field — output size
|
|
9697
|
+
lives on `size_bytes` / the downloads endpoint.
|
|
7669
9698
|
properties:
|
|
7670
9699
|
compression_ratio:
|
|
7671
9700
|
type: number
|
|
@@ -7706,6 +9735,7 @@ components:
|
|
|
7706
9735
|
|
|
7707
9736
|
WorkflowDownloadSuccessEnvelope:
|
|
7708
9737
|
type: object
|
|
9738
|
+
additionalProperties: false
|
|
7709
9739
|
required:
|
|
7710
9740
|
- success
|
|
7711
9741
|
- data
|
|
@@ -7759,9 +9789,10 @@ components:
|
|
|
7759
9789
|
not: { required: [page_index] }
|
|
7760
9790
|
- title: Unindexed
|
|
7761
9791
|
description: |
|
|
7762
|
-
Output without
|
|
7763
|
-
single-output download
|
|
7764
|
-
|
|
9792
|
+
Output without a page/position indexing field — every legacy
|
|
9793
|
+
single-output download AND `render_variants` variant outputs
|
|
9794
|
+
(which carry the optional `target_id` correlation field but are
|
|
9795
|
+
not page/position indexed).
|
|
7765
9796
|
not:
|
|
7766
9797
|
anyOf:
|
|
7767
9798
|
- required: [page_index]
|
|
@@ -7804,6 +9835,32 @@ components:
|
|
|
7804
9835
|
any current operation; declared for parity with
|
|
7805
9836
|
`OperationResultOutputEntry.position`. Per ADR-0009 §D2.
|
|
7806
9837
|
example: 0
|
|
9838
|
+
target_id:
|
|
9839
|
+
type: string
|
|
9840
|
+
pattern: '^[A-Za-z0-9._-]+$'
|
|
9841
|
+
maxLength: 64
|
|
9842
|
+
description: |
|
|
9843
|
+
The caller-assigned `id` of the `render_variants` target that
|
|
9844
|
+
produced this output, echoed verbatim (the image-fan-out
|
|
9845
|
+
addressing contract). Carried on the `Unindexed` branch (variant
|
|
9846
|
+
outputs are not page/position indexed); no operation emits
|
|
9847
|
+
`target_id` together with `page_index` / `position`. Mirrors
|
|
9848
|
+
`OperationResultOutputEntry.target_id`. Per ticket `w3EwzHYd`.
|
|
9849
|
+
example: "thumb-2x"
|
|
9850
|
+
node_id:
|
|
9851
|
+
type: string
|
|
9852
|
+
description: |
|
|
9853
|
+
Symbolic composition `node_id` correlating this download to its
|
|
9854
|
+
canonical node in `WorkflowCreateResponse.composition_plan` (e.g.
|
|
9855
|
+
`encode`, `thumbnail`, `processed_base`). Lets consumers label and
|
|
9856
|
+
group delivered files by composition role (e.g. sdks `byNode()`,
|
|
9857
|
+
FE "Main image" / "Thumbnail" labels). **Optional** — emitted once
|
|
9858
|
+
the canonicalization engine is live (optional-then-promote,
|
|
9859
|
+
mirroring `composition_plan` and `DeliveryPlanOutput.node_id`);
|
|
9860
|
+
absent until then. Additive carrier only; the normative
|
|
9861
|
+
`/downloads == delivery_plan.outputs[]` rendezvous invariant
|
|
9862
|
+
lands with the delivery-selection promotion, not here.
|
|
9863
|
+
example: thumbnail
|
|
7807
9864
|
|
|
7808
9865
|
# ============================================
|
|
7809
9866
|
# SSE EVENT SCHEMAS
|
|
@@ -7892,12 +9949,27 @@ components:
|
|
|
7892
9949
|
Total number of inputs expected in this phase. Pairs with
|
|
7893
9950
|
`phase_input_index`. For single-input operations this is
|
|
7894
9951
|
`1` during the relevant phase; for multi-input merge /
|
|
7895
|
-
archive / image_watermark / custom_luma / audio_overlay
|
|
9952
|
+
archive / image_watermark / custom_luma / audio_overlay /
|
|
9953
|
+
audio_to_video / video_watermark,
|
|
7896
9954
|
it matches the inputs[] count. Per ticket I27.
|
|
7897
9955
|
|
|
7898
9956
|
SseOperationCompletedData:
|
|
7899
9957
|
type: object
|
|
7900
|
-
description:
|
|
9958
|
+
description: |
|
|
9959
|
+
Payload for `operation.completed` events.
|
|
9960
|
+
|
|
9961
|
+
**`result` is intentionally OPTIONAL** (not in `required`) — the
|
|
9962
|
+
API does not always emit it on `operation.completed` (confirmed
|
|
9963
|
+
rmP7ndhK / api `Tu78AGpe`):
|
|
9964
|
+
- **Multi-output completions** (e.g. `split` → N outputs): the
|
|
9965
|
+
flat single-output `result` shape can't carry N outputs, so
|
|
9966
|
+
`result` is omitted by design and the outputs are read from
|
|
9967
|
+
the downloads endpoint (`GET /api/workflows/{id}/downloads`).
|
|
9968
|
+
- **Stale-load race**: a transient frame published before the
|
|
9969
|
+
operation resolves against the snapshot may omit `result`;
|
|
9970
|
+
it is repaired by a refetch.
|
|
9971
|
+
Consumers MUST treat `result` as may-be-absent on
|
|
9972
|
+
`operation.completed` and fall back to the downloads endpoint.
|
|
7901
9973
|
required:
|
|
7902
9974
|
- job_ref
|
|
7903
9975
|
- operation_id
|
|
@@ -8324,8 +10396,10 @@ components:
|
|
|
8324
10396
|
**Caching.** Per-tier private caching with ETag-based revalidation
|
|
8325
10397
|
(per ADR-0002). Public CDN caching is NOT used because the cache
|
|
8326
10398
|
key includes the caller's `user_tier`. Clients send
|
|
8327
|
-
`If-None-Match`
|
|
8328
|
-
|
|
10399
|
+
`If-None-Match` to revalidate; the server returns `304 Not
|
|
10400
|
+
Modified` when fresh. The content `ETag` is the sole conditional
|
|
10401
|
+
validator — `If-Modified-Since` is not honored and `Last-Modified`
|
|
10402
|
+
is informational only (per `sUyA9ZXD`).
|
|
8329
10403
|
|
|
8330
10404
|
Cache-key composition: `user_tier + schema_version + capabilities_version + environment`.
|
|
8331
10405
|
required:
|
|
@@ -8424,6 +10498,70 @@ components:
|
|
|
8424
10498
|
either source.
|
|
8425
10499
|
additionalProperties:
|
|
8426
10500
|
$ref: '#/components/schemas/EndpointProjection'
|
|
10501
|
+
workflow_features:
|
|
10502
|
+
type: object
|
|
10503
|
+
description: |
|
|
10504
|
+
Workflow-level option availability projection (ticket
|
|
10505
|
+
[`1ZQjSm0j`](https://trello.com/c/1ZQjSm0j)). Surfaces
|
|
10506
|
+
`per_value_availability` for workflow-level options that live in
|
|
10507
|
+
OpenAPI **component** schemas (not the per-operation
|
|
10508
|
+
`schemas/operations/*.yaml` that drive the `operations` map),
|
|
10509
|
+
keyed by REQUEST-shape path:
|
|
10510
|
+
- `delivery.mode.per_value_availability` (from the `Delivery.mode`
|
|
10511
|
+
schema)
|
|
10512
|
+
- `delivery.selection.type.per_value_availability` (from the
|
|
10513
|
+
`DeliverySelection.type` schema)
|
|
10514
|
+
|
|
10515
|
+
Closes the co0CERtJ (v2.19.0) authority-hierarchy gap: those
|
|
10516
|
+
`planned` downgrades lived only in the (decorative per ADR-0001
|
|
10517
|
+
§1.5) openapi component tags; this surfaces them in the
|
|
10518
|
+
authoritative sidecar.
|
|
10519
|
+
|
|
10520
|
+
**Optional in the wire envelope** (same incremental-mirroring
|
|
10521
|
+
stance as `endpoints`): the committed sidecar emits this block
|
|
10522
|
+
authoritatively; the runtime `GET /api/operations/schema` MAY
|
|
10523
|
+
mirror it once the API generator catches up. Consumers MUST
|
|
10524
|
+
tolerate its absence from the runtime endpoint (read the sidecar)
|
|
10525
|
+
and its presence from either source.
|
|
10526
|
+
properties:
|
|
10527
|
+
delivery:
|
|
10528
|
+
type: object
|
|
10529
|
+
properties:
|
|
10530
|
+
mode:
|
|
10531
|
+
type: object
|
|
10532
|
+
properties:
|
|
10533
|
+
per_value_availability:
|
|
10534
|
+
$ref: '#/components/schemas/PerValueAvailability'
|
|
10535
|
+
selection:
|
|
10536
|
+
type: object
|
|
10537
|
+
properties:
|
|
10538
|
+
type:
|
|
10539
|
+
type: object
|
|
10540
|
+
properties:
|
|
10541
|
+
per_value_availability:
|
|
10542
|
+
$ref: '#/components/schemas/PerValueAvailability'
|
|
10543
|
+
image_encode_capabilities:
|
|
10544
|
+
description: |
|
|
10545
|
+
Pre-flight image-encode capability matrix
|
|
10546
|
+
(`webp_quality_supported`, `background_flatten`, …) — **IDENTICAL
|
|
10547
|
+
shape to `composition_plan.capabilities`** on the create-workflow
|
|
10548
|
+
201, surfaced here so SDK/FE can fail-fast guard
|
|
10549
|
+
format/quality/alpha-flatten choices BEFORE submitting a workflow.
|
|
10550
|
+
Reuses the same API-side producer so the two can never disagree.
|
|
10551
|
+
|
|
10552
|
+
**Tier-invariant.** This is a fixed codec/hardware capability fact
|
|
10553
|
+
(what the image encoders CAN do), NOT an entitlement — identical
|
|
10554
|
+
for every tier including anonymous. Deliberately carries no
|
|
10555
|
+
`availability` tag, no `per_value_availability`, and does NOT vary
|
|
10556
|
+
by `user_tier` (even though the enclosing response is tier-scoped).
|
|
10557
|
+
|
|
10558
|
+
**Optional in the wire envelope** (same incremental-mirroring
|
|
10559
|
+
stance as `endpoints` / `workflow_features`): the committed sidecar
|
|
10560
|
+
MAY emit it; the runtime `GET /api/operations/schema` emits it once
|
|
10561
|
+
the API generator catches up. Consumers MUST tolerate its absence
|
|
10562
|
+
and its presence from either source. Per ticket
|
|
10563
|
+
[`kybXQe2S`](https://trello.com/c/kybXQe2S).
|
|
10564
|
+
$ref: '#/components/schemas/ImageEncodeCapabilities'
|
|
8427
10565
|
|
|
8428
10566
|
EndpointProjection:
|
|
8429
10567
|
type: object
|
|
@@ -8454,8 +10592,17 @@ components:
|
|
|
8454
10592
|
Value of the `x-identity-scoped` vendor extension on the
|
|
8455
10593
|
operation (default `false`). True iff the operation
|
|
8456
10594
|
targets an identity-bound resource and cross-identity
|
|
8457
|
-
access
|
|
10595
|
+
access is rejected — OR acts on the caller's implicit
|
|
8458
10596
|
identity-scoped data (credits balance, own session).
|
|
10597
|
+
|
|
10598
|
+
The rejection code is usually `403`, but some endpoints
|
|
10599
|
+
**existence-mask cross-identity references as `404`** (BOLA/
|
|
10600
|
+
IDOR: a 403 would leak that the resource exists). The
|
|
10601
|
+
`createWorkflow` upload reference is the canonical case —
|
|
10602
|
+
cross-owner uploads return `404 UPLOAD_NOT_FOUND`, never 403
|
|
10603
|
+
(per the ADR-0016 amendment). Consumers MUST NOT assume
|
|
10604
|
+
`identity_scoped: true` implies a 403 specifically; read the
|
|
10605
|
+
per-operation error responses for the exact code.
|
|
8459
10606
|
required_tier:
|
|
8460
10607
|
description: |
|
|
8461
10608
|
Endpoint-level entitlement gate. Reserved/null today —
|
|
@@ -8542,9 +10689,10 @@ components:
|
|
|
8542
10689
|
Optional per-role input-count overlay for role-based
|
|
8543
10690
|
multi-input operations. Per ticket
|
|
8544
10691
|
[`SlluxMBN`](https://trello.com/c/SlluxMBN) / ADR-0015.
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
custom_luma
|
|
10692
|
+
Present on all five role-based ops (audio_to_video,
|
|
10693
|
+
video_watermark, audio_overlay, image_watermark,
|
|
10694
|
+
custom_luma); absent on non-role ops (merge, archive)
|
|
10695
|
+
where all inputs share a single role.
|
|
8548
10696
|
accepts_mixed_types:
|
|
8549
10697
|
type: boolean
|
|
8550
10698
|
description: Whether mixed MIME types are allowed (archive only)
|
|
@@ -8628,6 +10776,37 @@ components:
|
|
|
8628
10776
|
[I3](https://trello.com/c/eCWIpug8); until then the
|
|
8629
10777
|
contract advertises the field shape but the endpoint
|
|
8630
10778
|
does not yet surface the field.
|
|
10779
|
+
processing_class:
|
|
10780
|
+
type: object
|
|
10781
|
+
description: |
|
|
10782
|
+
Optional per-mime-group processing-class block (per ticket
|
|
10783
|
+
[I15-CONS `YZpBKzOM`](https://trello.com/c/YZpBKzOM) + plan
|
|
10784
|
+
v5 §F8 long-form routing). Keyed by `ProcessingClass` enum
|
|
10785
|
+
value (`short_form` / `long_form` / `short_form_concat` /
|
|
10786
|
+
`long_form_re_encode`); each entry advertises that routing
|
|
10787
|
+
class's availability, tier gate, and size/duration caps.
|
|
10788
|
+
|
|
10789
|
+
Present only on mime_groups subject to short-form vs
|
|
10790
|
+
long-form routing — typically `video`; per F8 also `audio`
|
|
10791
|
+
for `audio_overlay` / `audio_watermark`. Absence means "no
|
|
10792
|
+
per-class routing exposed": consumers MUST treat absent as
|
|
10793
|
+
"no routing decision exposed" and MUST NOT coerce it to
|
|
10794
|
+
`short_form` (FORMAT.md §`processing_class` parser
|
|
10795
|
+
obligation 2 / ADR-0001 §1.4).
|
|
10796
|
+
|
|
10797
|
+
The runtime endpoint (`GET /api/operations/schema`) and the
|
|
10798
|
+
`availability/availability.json` sidecar emit this block
|
|
10799
|
+
verbatim from the source operation YAML (including any
|
|
10800
|
+
`per_tier_constraints` overlay — copied through as an
|
|
10801
|
+
opaque subtree). SDK + frontend consumers gate UI on the
|
|
10802
|
+
per-class `availability` + `required_tier` and surface the
|
|
10803
|
+
caller's effective caps as the binding limits. Surfacing
|
|
10804
|
+
this on the typed model retires the raw-ops-schema HTTP
|
|
10805
|
+
workaround per ticket
|
|
10806
|
+
[`yWeBr81O`](https://trello.com/c/yWeBr81O). See
|
|
10807
|
+
`schemas/FORMAT.md` §`processing_class:` block.
|
|
10808
|
+
additionalProperties:
|
|
10809
|
+
$ref: '#/components/schemas/ProcessingClassEntry'
|
|
8631
10810
|
options:
|
|
8632
10811
|
type: object
|
|
8633
10812
|
description: Options specific to this MIME group, keyed by option name
|
|
@@ -8731,6 +10910,104 @@ components:
|
|
|
8731
10910
|
The "set" sentinel means the option has any value. "logic" can be "and" (default) or "or".
|
|
8732
10911
|
additionalProperties: true
|
|
8733
10912
|
|
|
10913
|
+
ProcessingClassConstraints:
|
|
10914
|
+
type: object
|
|
10915
|
+
additionalProperties: false
|
|
10916
|
+
description: |
|
|
10917
|
+
Numeric size / duration caps for a single processing class (or
|
|
10918
|
+
a per-tier override of those caps). All fields optional; the
|
|
10919
|
+
`max_input_*` keys apply to single-input mime_groups while the
|
|
10920
|
+
`max_total_*` keys apply to multi-input merge-style mime_groups
|
|
10921
|
+
(a merge can have many small inputs whose combined size triggers
|
|
10922
|
+
long-form). Byte caps are positive integers; duration caps are
|
|
10923
|
+
ISO-8601 duration strings (e.g. `PT5M`). Field set is CI-fixed —
|
|
10924
|
+
`scripts/check-per-tier-constraints.py` rejects unknown keys and
|
|
10925
|
+
bad value types. See `schemas/FORMAT.md` §`constraints` sub-block.
|
|
10926
|
+
properties:
|
|
10927
|
+
max_input_duration:
|
|
10928
|
+
type: string
|
|
10929
|
+
description: |
|
|
10930
|
+
Max per-input duration as an ISO-8601 duration string.
|
|
10931
|
+
Single-input mime_groups.
|
|
10932
|
+
example: "PT5M"
|
|
10933
|
+
max_input_size_bytes:
|
|
10934
|
+
type: integer
|
|
10935
|
+
minimum: 1
|
|
10936
|
+
description: Max per-input file size in bytes. Single-input mime_groups.
|
|
10937
|
+
max_output_size_bytes:
|
|
10938
|
+
type: integer
|
|
10939
|
+
minimum: 1
|
|
10940
|
+
description: Max output file size in bytes. Any mime_group.
|
|
10941
|
+
max_total_duration:
|
|
10942
|
+
type: string
|
|
10943
|
+
description: |
|
|
10944
|
+
Max combined duration across all inputs as an ISO-8601
|
|
10945
|
+
duration string. Multi-input (merge) mime_groups.
|
|
10946
|
+
example: "PT1H"
|
|
10947
|
+
max_total_input_size_bytes:
|
|
10948
|
+
type: integer
|
|
10949
|
+
minimum: 1
|
|
10950
|
+
description: Max combined input size in bytes. Multi-input (merge) mime_groups.
|
|
10951
|
+
|
|
10952
|
+
ProcessingClassEntry:
|
|
10953
|
+
type: object
|
|
10954
|
+
description: |
|
|
10955
|
+
Single processing-class entry within a mime_group's
|
|
10956
|
+
`processing_class` map (per ticket
|
|
10957
|
+
[I15-CONS `YZpBKzOM`](https://trello.com/c/YZpBKzOM)). Mirrors
|
|
10958
|
+
`PerValueAvailabilityEntry` (availability + optional tier / eta /
|
|
10959
|
+
documentation_url) plus per-class `constraints` and an optional
|
|
10960
|
+
`per_tier_constraints` overlay. Surfaced on the typed getSchema
|
|
10961
|
+
model per ticket [`yWeBr81O`](https://trello.com/c/yWeBr81O).
|
|
10962
|
+
See `schemas/FORMAT.md` §`processing_class:` block.
|
|
10963
|
+
required:
|
|
10964
|
+
- availability
|
|
10965
|
+
properties:
|
|
10966
|
+
availability:
|
|
10967
|
+
$ref: '#/components/schemas/AvailabilityValue'
|
|
10968
|
+
description: Per-class availability tag.
|
|
10969
|
+
required_tier:
|
|
10970
|
+
description: |
|
|
10971
|
+
Tier required to use this class. Optional — omit when the
|
|
10972
|
+
class is gated by readiness rather than subscription.
|
|
10973
|
+
`null` is equivalent to omitted (no tier restriction).
|
|
10974
|
+
oneOf:
|
|
10975
|
+
- $ref: '#/components/schemas/UserTier'
|
|
10976
|
+
- type: 'null'
|
|
10977
|
+
eta:
|
|
10978
|
+
type: string
|
|
10979
|
+
description: |
|
|
10980
|
+
ISO-8601 date (`2026-09-15`) or quarter (`2026-Q3`) when
|
|
10981
|
+
this class is expected to ship. Only meaningful for
|
|
10982
|
+
`availability: planned`.
|
|
10983
|
+
documentation_url:
|
|
10984
|
+
type: string
|
|
10985
|
+
format: uri
|
|
10986
|
+
description: Optional link to processing-class documentation.
|
|
10987
|
+
constraints:
|
|
10988
|
+
$ref: '#/components/schemas/ProcessingClassConstraints'
|
|
10989
|
+
description: |
|
|
10990
|
+
Baseline caps for the lowest tier this class's
|
|
10991
|
+
`required_tier` permits. Overlaid per-caller by
|
|
10992
|
+
`per_tier_constraints` (see below).
|
|
10993
|
+
per_tier_constraints:
|
|
10994
|
+
type: object
|
|
10995
|
+
description: |
|
|
10996
|
+
Optional per-tier numeric-cap override map (per
|
|
10997
|
+
[ADR-0011](../docs/decisions/0011-per-tier-processing-class-constraints.md),
|
|
10998
|
+
ticket [`z4GDTUMx`](https://trello.com/c/z4GDTUMx)). Keys
|
|
10999
|
+
MUST be a subset of the `UserTier` enum and SHOULD name only
|
|
11000
|
+
tiers HIGHER than the class baseline (CI-enforced by
|
|
11001
|
+
`scripts/check-per-tier-constraints.py`). Each value
|
|
11002
|
+
overrides `constraints` field-by-field for callers of that
|
|
11003
|
+
tier. A consumer that ignores this map reads `constraints`
|
|
11004
|
+
and sees the smallest permitted-tier cap — it can never
|
|
11005
|
+
over-promise. `per_tier_constraints` sizes caps, it does
|
|
11006
|
+
NOT grant access (eligibility stays governed by
|
|
11007
|
+
`availability` + `required_tier`).
|
|
11008
|
+
additionalProperties:
|
|
11009
|
+
$ref: '#/components/schemas/ProcessingClassConstraints'
|
|
11010
|
+
|
|
8734
11011
|
# ============================================
|
|
8735
11012
|
# RETRY SCHEMAS
|
|
8736
11013
|
# ============================================
|
|
@@ -8756,6 +11033,7 @@ components:
|
|
|
8756
11033
|
|
|
8757
11034
|
RetrySuccessEnvelope:
|
|
8758
11035
|
type: object
|
|
11036
|
+
additionalProperties: false
|
|
8759
11037
|
required:
|
|
8760
11038
|
- success
|
|
8761
11039
|
- data
|