@giveitsmaller/contracts 0.9.0 → 0.17.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 +647 -60
- package/availability/availability.json +318 -94
- package/dist/asyncapi/Failure.d.ts +2 -0
- package/dist/asyncapi/LongFormOperationType.d.ts +2 -1
- package/dist/asyncapi/LongFormOperationType.js +1 -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 +2 -0
- package/dist/asyncapi/Unindexed.d.ts +1 -0
- package/dist/asyncapi/index.d.ts +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 +2 -2
- package/dist/openapi/models/CreateExternalImport422Response.js +2 -2
- package/dist/openapi/models/CreateWorkflow422Response.d.ts +2 -2
- package/dist/openapi/models/CreateWorkflow422Response.js +2 -2
- 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 +13 -2
- 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 +20 -4
- 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 +2 -2
- package/dist/openapi/models/JobResponse.js +2 -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 +7 -4
- package/dist/openapi/models/OperationType.js +8 -5
- package/dist/openapi/models/OperationsSchemaResponse.d.ts +32 -4
- package/dist/openapi/models/OperationsSchemaResponse.js +5 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeatures.d.ts +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeatures.js +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDelivery.d.ts +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDelivery.js +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliveryMode.d.ts +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliveryMode.js +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliverySelection.d.ts +2 -2
- package/dist/openapi/models/OperationsSchemaResponseWorkflowFeaturesDeliverySelection.js +2 -2
- 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 +13 -2
- 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 +13 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/openapi/models/ValidationErrorEnvelope.js +2 -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 +31 -1
- package/dist/openapi/models/index.js +31 -1
- package/dist/openapi/runtime.d.ts +2 -2
- package/dist/openapi/runtime.js +2 -2
- 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 -0
- 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 +19 -6
- package/openapi/api.yaml +2333 -241
- package/operations/schemas/archive.yaml +1 -1
- package/operations/schemas/audio_overlay.yaml +29 -13
- package/operations/schemas/audio_to_video.yaml +6 -5
- package/operations/schemas/audio_watermark.yaml +18 -16
- package/operations/schemas/compress.yaml +60 -35
- package/operations/schemas/convert.yaml +6 -1
- package/operations/schemas/custom_luma.yaml +18 -3
- package/operations/schemas/image_watermark.yaml +22 -7
- package/operations/schemas/merge.yaml +88 -35
- package/operations/schemas/passthrough.yaml +49 -0
- package/operations/schemas/render_variants.yaml +117 -0
- package/operations/schemas/split.yaml +78 -20
- 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 +7 -4
- package/package.json +3 -1
- 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.66.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: |
|
|
@@ -1149,10 +1203,17 @@ paths:
|
|
|
1149
1203
|
custom_luma, audio_overlay, audio_to_video, video_watermark):
|
|
1150
1204
|
provide `inputs[]` with
|
|
1151
1205
|
`JobInputV2` entries. Each entry has its own `source`
|
|
1152
|
-
(
|
|
1206
|
+
(a `MultiInputSource` — the 3-leaf subset of `WorkflowSource`
|
|
1207
|
+
that **excludes `upload`**: `job_output` / `external_import` /
|
|
1208
|
+
`connection`), plus optional `role`
|
|
1153
1209
|
(image_watermark, custom_luma, audio_overlay, audio_to_video,
|
|
1154
1210
|
video_watermark) and
|
|
1155
|
-
`per_input_options`.
|
|
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`.
|
|
1156
1217
|
|
|
1157
1218
|
Exactly one of `source` or `inputs` is required per job.
|
|
1158
1219
|
|
|
@@ -1184,6 +1245,26 @@ paths:
|
|
|
1184
1245
|
still receives the implicit `compress` — see above). Per
|
|
1185
1246
|
[ADR-0004](../docs/decisions/0004-job-shape.md).
|
|
1186
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
|
+
|
|
1187
1268
|
**Multi-input constraint:** multi-input jobs (`inputs[]`) must
|
|
1188
1269
|
have exactly one operation, which must be a multi-input type
|
|
1189
1270
|
(`merge`, `archive`, `image_watermark`, `custom_luma`,
|
|
@@ -1213,7 +1294,14 @@ paths:
|
|
|
1213
1294
|
is deferred per ADR-0001 §1.7 row 5.
|
|
1214
1295
|
operationId: createWorkflow
|
|
1215
1296
|
security: [{}, {bearerAuth: []}, {sessionAuth: []}] # optional (anon-OK on free-tier baseline; auth required for tier-gated operations)
|
|
1216
|
-
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
|
|
1217
1305
|
tags:
|
|
1218
1306
|
- Workflow
|
|
1219
1307
|
requestBody:
|
|
@@ -1235,6 +1323,26 @@ paths:
|
|
|
1235
1323
|
options:
|
|
1236
1324
|
mode: lossy
|
|
1237
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
|
|
1238
1346
|
v2_chain_compress_thumbnail:
|
|
1239
1347
|
summary: V2 chained compress -> thumbnail (id required on first job)
|
|
1240
1348
|
value:
|
|
@@ -1254,16 +1362,28 @@ paths:
|
|
|
1254
1362
|
width: 320
|
|
1255
1363
|
height: 240
|
|
1256
1364
|
v2_merge_two_uploads:
|
|
1257
|
-
summary: V2 multi-input merge (two uploads
|
|
1365
|
+
summary: V2 multi-input merge (two uploads via passthrough source jobs)
|
|
1258
1366
|
value:
|
|
1259
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
|
|
1260
1380
|
- inputs:
|
|
1261
1381
|
- source:
|
|
1262
|
-
type:
|
|
1263
|
-
|
|
1382
|
+
type: job_output
|
|
1383
|
+
from: src_a
|
|
1264
1384
|
- source:
|
|
1265
|
-
type:
|
|
1266
|
-
|
|
1385
|
+
type: job_output
|
|
1386
|
+
from: src_b
|
|
1267
1387
|
operations:
|
|
1268
1388
|
- type: merge
|
|
1269
1389
|
v2_external_import:
|
|
@@ -1534,14 +1654,36 @@ paths:
|
|
|
1534
1654
|
error_type: "balance_exhausted"
|
|
1535
1655
|
required_action: "wait_for_renewal"
|
|
1536
1656
|
'404':
|
|
1537
|
-
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".
|
|
1538
1677
|
content:
|
|
1539
1678
|
application/json:
|
|
1540
1679
|
schema:
|
|
1541
1680
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
1542
1681
|
example:
|
|
1543
1682
|
success: false
|
|
1544
|
-
error: "
|
|
1683
|
+
error: "UPLOAD_NOT_FOUND"
|
|
1684
|
+
message: "Upload not found"
|
|
1685
|
+
message_key: "upload.not_found"
|
|
1686
|
+
locale: "en-GB"
|
|
1545
1687
|
'422':
|
|
1546
1688
|
description: |
|
|
1547
1689
|
Semantically invalid request. One of:
|
|
@@ -2241,10 +2383,11 @@ paths:
|
|
|
2241
2383
|
|
|
2242
2384
|
**Per-tier private caching.** Per ADR-0002, the response is
|
|
2243
2385
|
per-caller; CDN-style public caching is NOT used. Clients
|
|
2244
|
-
revalidate via `ETag`
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
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.
|
|
2248
2391
|
|
|
2249
2392
|
Cache-key composition (server-side): `user_tier + schema_version + capabilities_version + environment`.
|
|
2250
2393
|
|
|
@@ -2289,9 +2432,11 @@ paths:
|
|
|
2289
2432
|
in: header
|
|
2290
2433
|
required: false
|
|
2291
2434
|
description: |
|
|
2292
|
-
|
|
2293
|
-
`
|
|
2294
|
-
|
|
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.
|
|
2295
2440
|
schema:
|
|
2296
2441
|
type: string
|
|
2297
2442
|
format: http-date
|
|
@@ -2300,7 +2445,7 @@ paths:
|
|
|
2300
2445
|
'200':
|
|
2301
2446
|
description: |
|
|
2302
2447
|
Operation schema (raw JSON, no ResponseEnvelope). Tier-scoped
|
|
2303
|
-
per caller; revalidate via `ETag`
|
|
2448
|
+
per caller; revalidate via the `ETag` (`If-None-Match`).
|
|
2304
2449
|
headers:
|
|
2305
2450
|
Cache-Control:
|
|
2306
2451
|
schema:
|
|
@@ -2323,8 +2468,10 @@ paths:
|
|
|
2323
2468
|
type: string
|
|
2324
2469
|
format: http-date
|
|
2325
2470
|
description: |
|
|
2326
|
-
HTTP-date of last response generation.
|
|
2327
|
-
|
|
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.
|
|
2328
2475
|
example: "Sun, 26 Apr 2026 09:00:00 GMT"
|
|
2329
2476
|
content:
|
|
2330
2477
|
application/json:
|
|
@@ -2385,10 +2532,10 @@ paths:
|
|
|
2385
2532
|
values: ["max", "crop", "scale"]
|
|
2386
2533
|
'304':
|
|
2387
2534
|
description: |
|
|
2388
|
-
Cached response is still fresh
|
|
2389
|
-
`
|
|
2390
|
-
|
|
2391
|
-
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.
|
|
2392
2539
|
headers:
|
|
2393
2540
|
Cache-Control:
|
|
2394
2541
|
schema:
|
|
@@ -2740,22 +2887,7 @@ paths:
|
|
|
2740
2887
|
content:
|
|
2741
2888
|
application/json:
|
|
2742
2889
|
schema:
|
|
2743
|
-
|
|
2744
|
-
required:
|
|
2745
|
-
- success
|
|
2746
|
-
- data
|
|
2747
|
-
properties:
|
|
2748
|
-
success:
|
|
2749
|
-
type: boolean
|
|
2750
|
-
enum: [true]
|
|
2751
|
-
data:
|
|
2752
|
-
type: object
|
|
2753
|
-
description: |
|
|
2754
|
-
Empty object on logout success — preserves the
|
|
2755
|
-
`{ success: true, data: {...} }` envelope
|
|
2756
|
-
invariant from `info.description`. Logout has
|
|
2757
|
-
no return payload; the data slot is always
|
|
2758
|
-
`{}`.
|
|
2890
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
2759
2891
|
example:
|
|
2760
2892
|
success: true
|
|
2761
2893
|
data: {}
|
|
@@ -2788,241 +2920,1270 @@ paths:
|
|
|
2788
2920
|
schema:
|
|
2789
2921
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2790
2922
|
|
|
2791
|
-
|
|
2792
|
-
# EXTERNAL IMPORT ENDPOINT (ticket I10)
|
|
2793
|
-
# ============================================
|
|
2794
|
-
|
|
2795
|
-
/api/external-imports:
|
|
2923
|
+
/api/auth/register:
|
|
2796
2924
|
post:
|
|
2797
|
-
summary: Register a
|
|
2925
|
+
summary: Register a new account
|
|
2798
2926
|
description: |
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
**Availability:** `planned` — runtime depends on cross-repo
|
|
2811
|
-
ticket [`MbosYtJD`](https://trello.com/c/MbosYtJD) (Lambda team
|
|
2812
|
-
publishes capability manifest) and the
|
|
2813
|
-
[`POST /api/connections`](https://trello.com/c/MbosYtJD) vault
|
|
2814
|
-
endpoint shipping. The contract surface ships now (contract-first
|
|
2815
|
-
per ADR-0001 §1.3); the runtime endpoint returns `404` until
|
|
2816
|
-
cross-repo wiring lands.
|
|
2817
|
-
operationId: createExternalImport
|
|
2818
|
-
# Auth: REQUIRED defensively. Endpoint is availability:planned (no
|
|
2819
|
-
# controller yet); but the endpoint stores encrypted server-side
|
|
2820
|
-
# secrets (bearer URLs, passwords) and anon-callable secret
|
|
2821
|
-
# storage is almost certainly wrong. Locking the contract here
|
|
2822
|
-
# while we still own the source of truth; runtime tightens to
|
|
2823
|
-
# match when the controller lands. Per ADR-0016 / karen review
|
|
2824
|
-
# on yN309QVb-B2.
|
|
2825
|
-
security: [{bearerAuth: []}, {sessionAuth: []}]
|
|
2826
|
-
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)
|
|
2827
2938
|
tags:
|
|
2828
|
-
-
|
|
2939
|
+
- Auth
|
|
2829
2940
|
requestBody:
|
|
2830
2941
|
required: true
|
|
2831
2942
|
content:
|
|
2832
2943
|
application/json:
|
|
2833
2944
|
schema:
|
|
2834
|
-
|
|
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"
|
|
2835
2961
|
responses:
|
|
2836
2962
|
'201':
|
|
2837
|
-
description:
|
|
2963
|
+
description: |
|
|
2964
|
+
Account created. A verification email has been dispatched; the
|
|
2965
|
+
account cannot authenticate until verified.
|
|
2838
2966
|
content:
|
|
2839
2967
|
application/json:
|
|
2840
2968
|
schema:
|
|
2841
|
-
$ref: '#/components/schemas/
|
|
2969
|
+
$ref: '#/components/schemas/EmptySuccessEnvelope'
|
|
2842
2970
|
example:
|
|
2843
2971
|
success: true
|
|
2844
|
-
data:
|
|
2845
|
-
external_source_id: "019539ab-2222-7000-8000-000000000001"
|
|
2846
|
-
expires_at: "2026-04-26T15:00:00Z"
|
|
2847
|
-
provider: "s3_presigned"
|
|
2972
|
+
data: {}
|
|
2848
2973
|
'400':
|
|
2849
|
-
description:
|
|
2974
|
+
description: Request body is invalid (malformed JSON, wrong field types, or body too large).
|
|
2850
2975
|
content:
|
|
2851
2976
|
application/json:
|
|
2852
2977
|
schema:
|
|
2853
2978
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2854
2979
|
example:
|
|
2855
2980
|
success: false
|
|
2856
|
-
error: "
|
|
2857
|
-
'403':
|
|
2858
|
-
description: |
|
|
2859
|
-
Forbidden — SSRF policy violation (private IP, loopback,
|
|
2860
|
-
cloud metadata endpoint), tier-quota restriction, or
|
|
2861
|
-
provider not enabled for caller's tier.
|
|
2862
|
-
content:
|
|
2863
|
-
application/json:
|
|
2864
|
-
schema:
|
|
2865
|
-
oneOf:
|
|
2866
|
-
- $ref: '#/components/schemas/TierRestrictionResponse'
|
|
2867
|
-
- $ref: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
2868
|
-
- $ref: '#/components/schemas/ErrorEnvelope'
|
|
2981
|
+
error: "Request body is invalid."
|
|
2869
2982
|
'422':
|
|
2870
2983
|
description: |
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
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.
|
|
2875
2990
|
content:
|
|
2876
2991
|
application/json:
|
|
2877
2992
|
schema:
|
|
2878
2993
|
oneOf:
|
|
2879
2994
|
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
2880
|
-
- $ref: '#/components/schemas/
|
|
2995
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
2881
2996
|
discriminator:
|
|
2882
2997
|
propertyName: error_type
|
|
2883
2998
|
mapping:
|
|
2884
2999
|
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
2885
|
-
|
|
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."
|
|
2886
3008
|
'429':
|
|
2887
|
-
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
|
|
2888
3020
|
content:
|
|
2889
3021
|
application/json:
|
|
2890
3022
|
schema:
|
|
2891
3023
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2892
3024
|
'500':
|
|
2893
|
-
description: Internal server error
|
|
3025
|
+
description: Internal server error.
|
|
2894
3026
|
content:
|
|
2895
3027
|
application/json:
|
|
2896
3028
|
schema:
|
|
2897
3029
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2898
3030
|
|
|
2899
|
-
|
|
2900
|
-
# AUDIO WATERMARK DECODE
|
|
2901
|
-
# ============================================
|
|
2902
|
-
|
|
2903
|
-
/api/audio-watermark/decode:
|
|
3031
|
+
/api/auth/verify-email:
|
|
2904
3032
|
post:
|
|
2905
|
-
summary:
|
|
3033
|
+
summary: Verify an account email address
|
|
2906
3034
|
description: |
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
`
|
|
2911
|
-
`
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
**Tier-restricted.** This endpoint is `enterprise`-only. Free
|
|
2915
|
-
and `pro` callers receive a 403 `feature_tier_restricted`.
|
|
2916
|
-
Anonymous callers receive a 401.
|
|
2917
|
-
|
|
2918
|
-
**Scope: own watermarks only.** The decoder will refuse to
|
|
2919
|
-
extract from media the caller did not mark themselves
|
|
2920
|
-
(server-side audit log of watermark origin per
|
|
2921
|
-
`watermark_id`). Per spike S11 acceptance — this avoids the
|
|
2922
|
-
privacy implications of arbitrary-media decode while still
|
|
2923
|
-
serving the forensic-tracking use case (find a leaked asset
|
|
2924
|
-
on a third-party platform, decode the watermark, look up the
|
|
2925
|
-
distribution channel that received the marked copy).
|
|
2926
|
-
|
|
2927
|
-
**Rate-limited.** Request rate per caller is enforced
|
|
2928
|
-
independently from the workflow-create rate limit; abusive
|
|
2929
|
-
decode patterns flag for audit per [ADR-0001](../docs/decisions/0001-contract-first-availability.md).
|
|
2930
|
-
|
|
2931
|
-
**`availability: planned`** — operation Lambda + decode
|
|
2932
|
-
Lambda are cross-repo follow-up. The endpoint declaration
|
|
2933
|
-
ships now (contract-first); the runtime returns
|
|
2934
|
-
`feature_not_available` (422) until the Lambda lands. Per
|
|
2935
|
-
Tension 1 (ADR-0001 §1.3).
|
|
2936
|
-
operationId: decodeAudioWatermark
|
|
2937
|
-
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)
|
|
2938
3042
|
tags:
|
|
2939
|
-
-
|
|
2940
|
-
x-availability: planned
|
|
3043
|
+
- Auth
|
|
2941
3044
|
requestBody:
|
|
2942
3045
|
required: true
|
|
2943
3046
|
content:
|
|
2944
3047
|
application/json:
|
|
2945
3048
|
schema:
|
|
2946
|
-
|
|
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"
|
|
2947
3059
|
responses:
|
|
2948
3060
|
'200':
|
|
2949
|
-
description:
|
|
2950
|
-
content:
|
|
2951
|
-
application/json:
|
|
2952
|
-
schema:
|
|
2953
|
-
$ref: '#/components/schemas/AudioWatermarkDecodeResponse'
|
|
2954
|
-
'401':
|
|
2955
|
-
description: Authentication required (anonymous callers).
|
|
2956
|
-
content:
|
|
2957
|
-
application/json:
|
|
2958
|
-
schema:
|
|
2959
|
-
$ref: '#/components/schemas/ErrorEnvelope'
|
|
2960
|
-
'403':
|
|
2961
|
-
description: |
|
|
2962
|
-
Tier insufficient (free / pro caller) — returned as
|
|
2963
|
-
`FeatureTierRestrictedResponse` with
|
|
2964
|
-
`error_type: feature_tier_restricted`. Per ADR-0001 §1.3.
|
|
3061
|
+
description: Email verified. The account can now authenticate.
|
|
2965
3062
|
content:
|
|
2966
3063
|
application/json:
|
|
2967
3064
|
schema:
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
tier_restriction: '#/components/schemas/TierRestrictionResponse'
|
|
2975
|
-
feature_tier_restricted: '#/components/schemas/FeatureTierRestrictedResponse'
|
|
2976
|
-
'404':
|
|
2977
|
-
description: |
|
|
2978
|
-
No watermark detected in the supplied asset, OR the
|
|
2979
|
-
detected watermark was not issued by this caller (the
|
|
2980
|
-
scope-limit applies — own-watermarks-only).
|
|
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).
|
|
2981
3071
|
content:
|
|
2982
3072
|
application/json:
|
|
2983
3073
|
schema:
|
|
2984
3074
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3075
|
+
example:
|
|
3076
|
+
success: false
|
|
3077
|
+
error: "Request body is invalid."
|
|
2985
3078
|
'422':
|
|
2986
3079
|
description: |
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
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.
|
|
2992
3086
|
content:
|
|
2993
3087
|
application/json:
|
|
2994
3088
|
schema:
|
|
2995
3089
|
oneOf:
|
|
2996
3090
|
- $ref: '#/components/schemas/ValidationErrorEnvelope'
|
|
2997
|
-
- $ref: '#/components/schemas/
|
|
3091
|
+
- $ref: '#/components/schemas/AuthRejectionEnvelope'
|
|
2998
3092
|
discriminator:
|
|
2999
3093
|
propertyName: error_type
|
|
3000
3094
|
mapping:
|
|
3001
3095
|
validation_error: '#/components/schemas/ValidationErrorEnvelope'
|
|
3002
|
-
|
|
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."
|
|
3003
3104
|
'429':
|
|
3004
3105
|
description: |
|
|
3005
|
-
Rate limit exceeded
|
|
3006
|
-
|
|
3007
|
-
|
|
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
|
|
3008
3115
|
content:
|
|
3009
3116
|
application/json:
|
|
3010
3117
|
schema:
|
|
3011
3118
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3012
3119
|
'500':
|
|
3013
|
-
description: Internal server error
|
|
3120
|
+
description: Internal server error.
|
|
3014
3121
|
content:
|
|
3015
3122
|
application/json:
|
|
3016
3123
|
schema:
|
|
3017
3124
|
$ref: '#/components/schemas/ErrorEnvelope'
|
|
3018
3125
|
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
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'
|
|
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'
|
|
4164
|
+
'429':
|
|
4165
|
+
description: |
|
|
4166
|
+
Rate limit exceeded for decode requests. Decode
|
|
4167
|
+
rate-limit is enforced independently from
|
|
4168
|
+
workflow-create.
|
|
4169
|
+
content:
|
|
4170
|
+
application/json:
|
|
4171
|
+
schema:
|
|
4172
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4173
|
+
'500':
|
|
4174
|
+
description: Internal server error
|
|
4175
|
+
content:
|
|
4176
|
+
application/json:
|
|
4177
|
+
schema:
|
|
4178
|
+
$ref: '#/components/schemas/ErrorEnvelope'
|
|
4179
|
+
|
|
4180
|
+
# ============================================
|
|
4181
|
+
# CREDITS + BILLING ENDPOINTS (F9 — per ticket I23)
|
|
4182
|
+
# ============================================
|
|
4183
|
+
|
|
4184
|
+
/api/v2/credits/balance:
|
|
4185
|
+
get:
|
|
4186
|
+
summary: Get current credit balance
|
|
3026
4187
|
description: |
|
|
3027
4188
|
Returns a snapshot of the caller's credit position at request
|
|
3028
4189
|
time. Per ticket [I23 `DffjC3zm`](https://trello.com/c/DffjC3zm)
|
|
@@ -3469,6 +4630,37 @@ components:
|
|
|
3469
4630
|
success:
|
|
3470
4631
|
type: boolean
|
|
3471
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
|
+
|
|
3472
4664
|
ErrorEnvelope:
|
|
3473
4665
|
type: object
|
|
3474
4666
|
description: |
|
|
@@ -3525,6 +4717,17 @@ components:
|
|
|
3525
4717
|
would exceed the S3 multipart capacity cap. Pre-S3
|
|
3526
4718
|
server-side capacity gate; distinct from tier-quota
|
|
3527
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.
|
|
3528
4731
|
message:
|
|
3529
4732
|
type: string
|
|
3530
4733
|
description: |
|
|
@@ -3691,6 +4894,82 @@ components:
|
|
|
3691
4894
|
`ErrorEnvelope.message_params`. Excludes cost
|
|
3692
4895
|
numbers.
|
|
3693
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
|
+
|
|
3694
4973
|
ReEncodeDecision:
|
|
3695
4974
|
type: string
|
|
3696
4975
|
description: |
|
|
@@ -3849,11 +5128,13 @@ components:
|
|
|
3849
5128
|
— this is the load-bearing invariant that makes the
|
|
3850
5129
|
primitive useful.
|
|
3851
5130
|
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
prose
|
|
3856
|
-
|
|
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.
|
|
3857
5138
|
|
|
3858
5139
|
Per ticket [`SlluxMBN`](https://trello.com/c/SlluxMBN) /
|
|
3859
5140
|
ADR-0015. Consumed by `OperationInputRoleValidator` in
|
|
@@ -3892,8 +5173,10 @@ components:
|
|
|
3892
5173
|
# ============================================
|
|
3893
5174
|
#
|
|
3894
5175
|
# Per ADR-0005. WorkflowSource is the discriminated union consumed
|
|
3895
|
-
# by V2 JobDefinition.source
|
|
3896
|
-
#
|
|
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.
|
|
3897
5180
|
|
|
3898
5181
|
UploadSource:
|
|
3899
5182
|
type: object
|
|
@@ -3985,8 +5268,11 @@ components:
|
|
|
3985
5268
|
|
|
3986
5269
|
WorkflowSource:
|
|
3987
5270
|
description: |
|
|
3988
|
-
Discriminated union of source leaves
|
|
3989
|
-
`
|
|
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
|
|
3990
5276
|
[ADR-0005](../docs/decisions/0005-external-sources.md) +
|
|
3991
5277
|
[ADR-0004](../docs/decisions/0004-job-shape.md).
|
|
3992
5278
|
|
|
@@ -4005,6 +5291,30 @@ components:
|
|
|
4005
5291
|
external_import: '#/components/schemas/ExternalImportToken'
|
|
4006
5292
|
connection: '#/components/schemas/ConnectionSource'
|
|
4007
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
|
+
|
|
4008
5318
|
ExternalSource:
|
|
4009
5319
|
description: |
|
|
4010
5320
|
Subset alias of `WorkflowSource` that excludes uploads and
|
|
@@ -4324,6 +5634,7 @@ components:
|
|
|
4324
5634
|
|
|
4325
5635
|
CreditsBalanceSuccessEnvelope:
|
|
4326
5636
|
type: object
|
|
5637
|
+
additionalProperties: false
|
|
4327
5638
|
required:
|
|
4328
5639
|
- success
|
|
4329
5640
|
- data
|
|
@@ -4494,6 +5805,7 @@ components:
|
|
|
4494
5805
|
|
|
4495
5806
|
CreditsUsageSuccessEnvelope:
|
|
4496
5807
|
type: object
|
|
5808
|
+
additionalProperties: false
|
|
4497
5809
|
required:
|
|
4498
5810
|
- success
|
|
4499
5811
|
- data
|
|
@@ -4651,6 +5963,7 @@ components:
|
|
|
4651
5963
|
|
|
4652
5964
|
WorkflowCancelSuccessEnvelope:
|
|
4653
5965
|
type: object
|
|
5966
|
+
additionalProperties: false
|
|
4654
5967
|
required:
|
|
4655
5968
|
- success
|
|
4656
5969
|
- data
|
|
@@ -4686,6 +5999,7 @@ components:
|
|
|
4686
5999
|
|
|
4687
6000
|
WorkflowResumeSuccessEnvelope:
|
|
4688
6001
|
type: object
|
|
6002
|
+
additionalProperties: false
|
|
4689
6003
|
required:
|
|
4690
6004
|
- success
|
|
4691
6005
|
- data
|
|
@@ -5595,7 +6909,7 @@ components:
|
|
|
5595
6909
|
- thumbnail_office: Office document (DOCX/XLSX/PPTX/ODT/ODS/ODP)
|
|
5596
6910
|
thumbnail sub-type. Backed by a dedicated LibreOffice Lambda.
|
|
5597
6911
|
Emitted by the publisher for office-document inputs.
|
|
5598
|
-
- image_watermark: Image overlay
|
|
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).
|
|
5599
6913
|
- text_watermark: Text overlay rendered onto an image (Liberation Sans). Single-input. Per ADR-0004 + I4-CONS.
|
|
5600
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.
|
|
5601
6915
|
- archive: Bundle files into ZIP/tar.gz (all types). Multi-input.
|
|
@@ -5606,7 +6920,8 @@ components:
|
|
|
5606
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).
|
|
5607
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.
|
|
5608
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.
|
|
5609
|
-
- 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`
|
|
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 BOTH classes (`video.processing_class.short_form: beta` AND `long_form: beta` — `split-video-fargate` proven on staging per [`rcwvUKhI`](https://trello.com/c/rcwvUKhI)). 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).
|
|
5610
6925
|
|
|
5611
6926
|
Both the legacy `thumbnail` value and the four sub-type values
|
|
5612
6927
|
are valid routing targets today during the thumbnail migration
|
|
@@ -5625,6 +6940,7 @@ components:
|
|
|
5625
6940
|
- thumbnail_office
|
|
5626
6941
|
- image_watermark
|
|
5627
6942
|
- text_watermark
|
|
6943
|
+
- render_variants
|
|
5628
6944
|
- merge
|
|
5629
6945
|
- archive
|
|
5630
6946
|
- convert
|
|
@@ -5635,6 +6951,7 @@ components:
|
|
|
5635
6951
|
- video_watermark
|
|
5636
6952
|
- video_text_watermark
|
|
5637
6953
|
- split
|
|
6954
|
+
- passthrough
|
|
5638
6955
|
|
|
5639
6956
|
OperationInputModel:
|
|
5640
6957
|
type: string
|
|
@@ -5643,7 +6960,7 @@ components:
|
|
|
5643
6960
|
- single: One input file (compress, thumbnail, thumbnail_image,
|
|
5644
6961
|
thumbnail_video, thumbnail_document, thumbnail_office,
|
|
5645
6962
|
text_watermark, convert, audio_watermark,
|
|
5646
|
-
video_text_watermark, split)
|
|
6963
|
+
video_text_watermark, split, passthrough)
|
|
5647
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.
|
|
5648
6965
|
enum:
|
|
5649
6966
|
- single
|
|
@@ -5811,6 +7128,7 @@ components:
|
|
|
5811
7128
|
|
|
5812
7129
|
UploadSuccessEnvelope:
|
|
5813
7130
|
type: object
|
|
7131
|
+
additionalProperties: false
|
|
5814
7132
|
required:
|
|
5815
7133
|
- success
|
|
5816
7134
|
- data
|
|
@@ -6423,6 +7741,7 @@ components:
|
|
|
6423
7741
|
|
|
6424
7742
|
MetadataSuccessEnvelope:
|
|
6425
7743
|
type: object
|
|
7744
|
+
additionalProperties: false
|
|
6426
7745
|
required:
|
|
6427
7746
|
- success
|
|
6428
7747
|
- data
|
|
@@ -6439,15 +7758,82 @@ components:
|
|
|
6439
7758
|
|
|
6440
7759
|
WorkflowCreateRequest:
|
|
6441
7760
|
type: object
|
|
6442
|
-
|
|
6443
|
-
-
|
|
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]
|
|
6444
7806
|
properties:
|
|
6445
7807
|
jobs:
|
|
6446
7808
|
type: array
|
|
6447
|
-
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.
|
|
6448
7813
|
minItems: 1
|
|
6449
7814
|
items:
|
|
6450
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'
|
|
6451
7837
|
workflow_edges:
|
|
6452
7838
|
type: array
|
|
6453
7839
|
description: |
|
|
@@ -6596,7 +7982,10 @@ components:
|
|
|
6596
7982
|
Multi-input list for `merge`, `archive`, `image_watermark`,
|
|
6597
7983
|
`custom_luma`, `audio_overlay`, `audio_to_video`, and
|
|
6598
7984
|
`video_watermark`. Each
|
|
6599
|
-
entry is a `JobInputV2` with its own `
|
|
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`).
|
|
6600
7989
|
Mutually exclusive with `source` — the V2 shape boundary
|
|
6601
7990
|
stays `source` (single-input) XOR `inputs[]` (multi-input
|
|
6602
7991
|
role-based) per ADR-0004 / I12.
|
|
@@ -6615,10 +8004,23 @@ components:
|
|
|
6615
8004
|
operations:
|
|
6616
8005
|
type: array
|
|
6617
8006
|
description: |
|
|
6618
|
-
|
|
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
|
|
6619
8013
|
operation consumes the previous operation's output; all
|
|
6620
8014
|
intermediate and final outputs are kept.
|
|
6621
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
|
+
|
|
6622
8024
|
Multi-input jobs (with `inputs[]`) must have exactly one
|
|
6623
8025
|
operation, and it must be a multi-input type (`merge`,
|
|
6624
8026
|
`archive`, `image_watermark`, `custom_luma`,
|
|
@@ -6669,21 +8071,66 @@ components:
|
|
|
6669
8071
|
via Wave A [`c3uthIP4`](https://trello.com/c/c3uthIP4) —
|
|
6670
8072
|
`availability: beta`, backends live). The multi-input
|
|
6671
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.
|
|
6672
8115
|
|
|
6673
8116
|
JobInputV2:
|
|
6674
8117
|
type: object
|
|
6675
8118
|
description: |
|
|
6676
8119
|
V2 multi-input entry per [ADR-0004](../docs/decisions/0004-job-shape.md)
|
|
6677
8120
|
§"JobInputV2". Replaces V1 `JobInput`. Each entry carries its own
|
|
6678
|
-
`source` (a `
|
|
6679
|
-
`
|
|
6680
|
-
external imports, vault connections, and upstream job
|
|
6681
|
-
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)).
|
|
6682
8129
|
required:
|
|
6683
8130
|
- source
|
|
6684
8131
|
properties:
|
|
6685
8132
|
source:
|
|
6686
|
-
$ref: '#/components/schemas/
|
|
8133
|
+
$ref: '#/components/schemas/MultiInputSource'
|
|
6687
8134
|
role:
|
|
6688
8135
|
type: string
|
|
6689
8136
|
description: |
|
|
@@ -6757,6 +8204,24 @@ components:
|
|
|
6757
8204
|
rules. For example, `quality` is only valid when `mode: lossy` for
|
|
6758
8205
|
compress operations.
|
|
6759
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.
|
|
6760
8225
|
|
|
6761
8226
|
# ============================================
|
|
6762
8227
|
# WORKFLOW DELIVERY (ticket I8-CONS)
|
|
@@ -6787,6 +8252,13 @@ components:
|
|
|
6787
8252
|
Optional operation type filter when the job has multiple
|
|
6788
8253
|
operations. Omit to select the last operation's output.
|
|
6789
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
|
+
|
|
6790
8262
|
DeliverySelection:
|
|
6791
8263
|
type: object
|
|
6792
8264
|
description: |
|
|
@@ -6802,8 +8274,10 @@ components:
|
|
|
6802
8274
|
Selection strategy:
|
|
6803
8275
|
- `terminal` (default): bundle all jobs with no outgoing
|
|
6804
8276
|
edges (computed from `JobOutputSource.from` references).
|
|
6805
|
-
- `all_outputs`: bundle every
|
|
6806
|
-
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`).
|
|
6807
8281
|
- `explicit`: bundle only the refs listed in `refs[]`.
|
|
6808
8282
|
Mutually exclusive with per-job `JobDefinition.deliver: true`
|
|
6809
8283
|
(validator rejects 422 if both are present).
|
|
@@ -6813,19 +8287,21 @@ components:
|
|
|
6813
8287
|
- explicit
|
|
6814
8288
|
per_value_availability:
|
|
6815
8289
|
terminal: { availability: stable }
|
|
6816
|
-
all_outputs: { availability:
|
|
6817
|
-
explicit: { availability:
|
|
6818
|
-
# Availability flipped
|
|
6819
|
-
# [`
|
|
6820
|
-
#
|
|
6821
|
-
#
|
|
6822
|
-
#
|
|
6823
|
-
#
|
|
6824
|
-
# threads the request-level
|
|
6825
|
-
# flips back
|
|
6826
|
-
#
|
|
6827
|
-
#
|
|
6828
|
-
#
|
|
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).
|
|
6829
8305
|
refs:
|
|
6830
8306
|
type: array
|
|
6831
8307
|
description: |
|
|
@@ -6953,6 +8429,18 @@ components:
|
|
|
6953
8429
|
description: |
|
|
6954
8430
|
Set when `reason: consumed_by`. The id of the downstream
|
|
6955
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.
|
|
6956
8444
|
|
|
6957
8445
|
DeliveryPlan:
|
|
6958
8446
|
type: object
|
|
@@ -7211,6 +8699,205 @@ components:
|
|
|
7211
8699
|
class_hint:
|
|
7212
8700
|
$ref: '#/components/schemas/ProcessingClassHint'
|
|
7213
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
|
+
|
|
7214
8901
|
# ============================================
|
|
7215
8902
|
# UPLOAD PREFLIGHT PROBE (per ticket I28)
|
|
7216
8903
|
# ============================================
|
|
@@ -7416,6 +9103,7 @@ components:
|
|
|
7416
9103
|
|
|
7417
9104
|
UploadProbeSuccessEnvelope:
|
|
7418
9105
|
type: object
|
|
9106
|
+
additionalProperties: false
|
|
7419
9107
|
description: |
|
|
7420
9108
|
Success envelope wrapping `UploadProbeResponse` on
|
|
7421
9109
|
`POST /api/uploads/{id}/probe` 200 responses, per ticket
|
|
@@ -7603,6 +9291,24 @@ components:
|
|
|
7603
9291
|
frontend can preview routing decisions before the
|
|
7604
9292
|
workflow runs. Estimation algorithm is server-side and
|
|
7605
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.
|
|
7606
9312
|
warnings:
|
|
7607
9313
|
type: array
|
|
7608
9314
|
description: |
|
|
@@ -7640,6 +9346,7 @@ components:
|
|
|
7640
9346
|
|
|
7641
9347
|
WorkflowCreateSuccessEnvelope:
|
|
7642
9348
|
type: object
|
|
9349
|
+
additionalProperties: false
|
|
7643
9350
|
required:
|
|
7644
9351
|
- success
|
|
7645
9352
|
- data
|
|
@@ -7691,9 +9398,25 @@ components:
|
|
|
7691
9398
|
(per ticket [I24](https://trello.com/c/e50uXLcl)). Carries
|
|
7692
9399
|
`paused_at`, `expires_at`, `required_action`, and
|
|
7693
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`.
|
|
7694
9416
|
|
|
7695
9417
|
WorkflowStatusSuccessEnvelope:
|
|
7696
9418
|
type: object
|
|
9419
|
+
additionalProperties: false
|
|
7697
9420
|
required:
|
|
7698
9421
|
- success
|
|
7699
9422
|
- data
|
|
@@ -7704,6 +9427,118 @@ components:
|
|
|
7704
9427
|
data:
|
|
7705
9428
|
$ref: '#/components/schemas/WorkflowStatusResponse'
|
|
7706
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
|
+
|
|
7707
9542
|
JobResponse:
|
|
7708
9543
|
type: object
|
|
7709
9544
|
description: Job status within a workflow response
|
|
@@ -7771,6 +9606,18 @@ components:
|
|
|
7771
9606
|
description: Progress percentage (0-100). Present when in_progress or completed.
|
|
7772
9607
|
result:
|
|
7773
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).
|
|
7774
9621
|
error_code:
|
|
7775
9622
|
type: string
|
|
7776
9623
|
description: |
|
|
@@ -7787,6 +9634,32 @@ components:
|
|
|
7787
9634
|
absent otherwise. Mirrors `SseOperationFailedData.error_message`.
|
|
7788
9635
|
example: "output_too_large: Output (12156489 bytes) is not smaller than input (6187609 bytes)"
|
|
7789
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
|
+
|
|
7790
9663
|
OperationResult:
|
|
7791
9664
|
type: object
|
|
7792
9665
|
description: |
|
|
@@ -7808,7 +9681,20 @@ components:
|
|
|
7808
9681
|
description: Key in the customer's export destination (if export configured)
|
|
7809
9682
|
metrics:
|
|
7810
9683
|
type: object
|
|
7811
|
-
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.
|
|
7812
9698
|
properties:
|
|
7813
9699
|
compression_ratio:
|
|
7814
9700
|
type: number
|
|
@@ -7849,6 +9735,7 @@ components:
|
|
|
7849
9735
|
|
|
7850
9736
|
WorkflowDownloadSuccessEnvelope:
|
|
7851
9737
|
type: object
|
|
9738
|
+
additionalProperties: false
|
|
7852
9739
|
required:
|
|
7853
9740
|
- success
|
|
7854
9741
|
- data
|
|
@@ -7902,9 +9789,10 @@ components:
|
|
|
7902
9789
|
not: { required: [page_index] }
|
|
7903
9790
|
- title: Unindexed
|
|
7904
9791
|
description: |
|
|
7905
|
-
Output without
|
|
7906
|
-
single-output download
|
|
7907
|
-
|
|
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).
|
|
7908
9796
|
not:
|
|
7909
9797
|
anyOf:
|
|
7910
9798
|
- required: [page_index]
|
|
@@ -7947,6 +9835,32 @@ components:
|
|
|
7947
9835
|
any current operation; declared for parity with
|
|
7948
9836
|
`OperationResultOutputEntry.position`. Per ADR-0009 §D2.
|
|
7949
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
|
|
7950
9864
|
|
|
7951
9865
|
# ============================================
|
|
7952
9866
|
# SSE EVENT SCHEMAS
|
|
@@ -8041,7 +9955,21 @@ components:
|
|
|
8041
9955
|
|
|
8042
9956
|
SseOperationCompletedData:
|
|
8043
9957
|
type: object
|
|
8044
|
-
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.
|
|
8045
9973
|
required:
|
|
8046
9974
|
- job_ref
|
|
8047
9975
|
- operation_id
|
|
@@ -8468,8 +10396,10 @@ components:
|
|
|
8468
10396
|
**Caching.** Per-tier private caching with ETag-based revalidation
|
|
8469
10397
|
(per ADR-0002). Public CDN caching is NOT used because the cache
|
|
8470
10398
|
key includes the caller's `user_tier`. Clients send
|
|
8471
|
-
`If-None-Match`
|
|
8472
|
-
|
|
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`).
|
|
8473
10403
|
|
|
8474
10404
|
Cache-key composition: `user_tier + schema_version + capabilities_version + environment`.
|
|
8475
10405
|
required:
|
|
@@ -8610,6 +10540,28 @@ components:
|
|
|
8610
10540
|
properties:
|
|
8611
10541
|
per_value_availability:
|
|
8612
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'
|
|
8613
10565
|
|
|
8614
10566
|
EndpointProjection:
|
|
8615
10567
|
type: object
|
|
@@ -8640,8 +10592,17 @@ components:
|
|
|
8640
10592
|
Value of the `x-identity-scoped` vendor extension on the
|
|
8641
10593
|
operation (default `false`). True iff the operation
|
|
8642
10594
|
targets an identity-bound resource and cross-identity
|
|
8643
|
-
access
|
|
10595
|
+
access is rejected — OR acts on the caller's implicit
|
|
8644
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.
|
|
8645
10606
|
required_tier:
|
|
8646
10607
|
description: |
|
|
8647
10608
|
Endpoint-level entitlement gate. Reserved/null today —
|
|
@@ -8728,9 +10689,10 @@ components:
|
|
|
8728
10689
|
Optional per-role input-count overlay for role-based
|
|
8729
10690
|
multi-input operations. Per ticket
|
|
8730
10691
|
[`SlluxMBN`](https://trello.com/c/SlluxMBN) / ADR-0015.
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
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.
|
|
8734
10696
|
accepts_mixed_types:
|
|
8735
10697
|
type: boolean
|
|
8736
10698
|
description: Whether mixed MIME types are allowed (archive only)
|
|
@@ -8814,6 +10776,37 @@ components:
|
|
|
8814
10776
|
[I3](https://trello.com/c/eCWIpug8); until then the
|
|
8815
10777
|
contract advertises the field shape but the endpoint
|
|
8816
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'
|
|
8817
10810
|
options:
|
|
8818
10811
|
type: object
|
|
8819
10812
|
description: Options specific to this MIME group, keyed by option name
|
|
@@ -8917,6 +10910,104 @@ components:
|
|
|
8917
10910
|
The "set" sentinel means the option has any value. "logic" can be "and" (default) or "or".
|
|
8918
10911
|
additionalProperties: true
|
|
8919
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
|
+
|
|
8920
11011
|
# ============================================
|
|
8921
11012
|
# RETRY SCHEMAS
|
|
8922
11013
|
# ============================================
|
|
@@ -8942,6 +11033,7 @@ components:
|
|
|
8942
11033
|
|
|
8943
11034
|
RetrySuccessEnvelope:
|
|
8944
11035
|
type: object
|
|
11036
|
+
additionalProperties: false
|
|
8945
11037
|
required:
|
|
8946
11038
|
- success
|
|
8947
11039
|
- data
|