> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://mediakind.ferndocs.com/platform/how-to/media/transforms-and-jobs/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://mediakind.ferndocs.com/_mcp/server. # Transforms and jobs A transform describes what processing should happen. A job applies that processing to a specific input. Together they are the core of Video on Demand (VOD) processing in the Media API. A transform is a recipe you write once; a job runs that recipe against an asset and writes one or more output assets. You usually create a small number of transforms and reuse them across many jobs. ## Create a transform A transform is created with `PUT`. Its `outputs` array holds the preset that defines the processing. The example below builds an adaptive-bitrate encoding transform using a built-in encoder preset. * Terminal window ```bash curl -X PUT "https://app.mk.io/api/v1/projects//media/transforms/abr-720p" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "properties": { "description": "H.264 720p multi-bitrate encoding", "outputs": [ { "preset": { "@odata.type": "#Microsoft.Media.BuiltInStandardEncoderPreset", "presetName": "H264MultipleBitrate720p" }, "onError": "StopProcessingJob", "relativePriority": "Normal" } ] } }' ``` Each output also accepts `onError` (`ContinueJob` or `StopProcessingJob`) and `relativePriority` (`High`, `Normal`, or `Low`). ## Choosing a preset The preset is selected by `@odata.type`. The most common choice is the built-in encoder, but the Media API also offers format conversion, thumbnails, track insertion, and an Artificial Intelligence (AI) pipeline. | To… | Use preset `@odata.type` | Key field | | ----------------------------------------------- | ----------------------------------------------- | ------------ | | Encode to adaptive bitrate H.264 or H.265 | `#Microsoft.Media.BuiltInStandardEncoderPreset` | `presetName` | | Convert or repackage to MP4 without re-encoding | `#Microsoft.Media.BuiltInAssetConverterPreset` | `presetName` | | Generate thumbnails | `#MediaKind.ThumbnailGeneratorPreset` | `thumbnails` | | Insert a track (for example, captions) | `#MediaKind.TrackInserterPreset` | `tracks` | | Run an AI pipeline (transcription, translation) | `#MediaKind.AIPipelinePreset` | `pipeline` | For the built-in encoder, `presetName` chooses the encoding ladder. Single-bitrate options include `H264SingleBitrate720p`, `H264SingleBitrate1080p`, `H265SingleBitrate1080p`, and `H265SingleBitrate4K`. Multi-bitrate options include `H264MultipleBitrateSD`, `H264MultipleBitrate720p`, and `H264MultipleBitrate1080p`, each also available in a `WithCVQ` variant. The Media API reference lists the complete enum. ## Run a job A job is created with `PUT` under the transform. It needs an `input` and `outputs`. The input is discriminated by `@odata.type`: use `JobInputAsset` to process a stored asset, or `JobInputHttp` to pull from an external HTTP source. Outputs are always `JobOutputAsset`. ```bash curl -X PUT "https://app.mk.io/api/v1/projects//media/transforms/abr-720p/jobs/job-001" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "properties": { "input": { "@odata.type": "#Microsoft.Media.JobInputAsset", "assetName": "input-video", "files": ["video.mp4"] }, "outputs": [ { "@odata.type": "#Microsoft.Media.JobOutputAsset", "assetName": "output-video" } ], "description": "Encode input-video with the 720p transform", "priority": "Normal" } }' ``` The output asset named here is created and populated by the job. It does not need to exist beforehand. ## Clip the input `JobInputAsset` accepts `start` and `end` clip times. Use `AbsoluteClipTime` with an ISO 8601 duration to process only part of the source: ```bash curl -X PUT "https://app.mk.io/api/v1/projects//media/transforms/abr-720p/jobs/clip-job" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "properties": { "input": { "@odata.type": "#Microsoft.Media.JobInputAsset", "assetName": "input-video", "start": { "@odata.type": "#Microsoft.Media.AbsoluteClipTime", "time": "PT0S" }, "end": { "@odata.type": "#Microsoft.Media.AbsoluteClipTime", "time": "PT30S" } }, "outputs": [ { "@odata.type": "#Microsoft.Media.JobOutputAsset", "assetName": "clip-output" } ] } }' ``` ## Monitor the job Poll the job state endpoint while you wait: ```bash curl -X GET "https://app.mk.io/api/v1/projects//media/transforms/abr-720p/jobs/job-001/state" \ -H "Authorization: Bearer " ``` A job moves through these states: | State | Meaning | | ------------ | -------------------------------- | | `Queued` | Waiting to be processed. | | `Scheduled` | Accepted and scheduled for work. | | `Processing` | Running. | | `Finished` | Completed successfully. | | `Error` | Completed with an error. | | `Canceling` | Cancellation is in progress. | | `Canceled` | Cancelled. | For background workers and high-volume pipelines, subscribe to the `MediaKind.JobStarted` and `MediaKind.JobFinished` webhooks instead of polling. ## Cancel a job Cancel a job that is no longer needed: ```bash curl -X POST "https://app.mk.io/api/v1/projects//media/transforms/abr-720p/jobs/job-001/cancelJob" \ -H "Authorization: Bearer " ``` ## What goes wrong An invalid `presetName`. `presetName` must be one of the documented enum values, such as `H264MultipleBitrate720p`. A made-up name like `AdaptiveStreaming` is rejected. Check the Media API reference for the exact list. * Publishing the input asset instead of the output. A job writes to the output asset. Publish that one, not the source. * A job reaches `Error`. The job output object carries an `error` field with the detail. Read the full job, not just `/state`, to see it. ## What comes next * Automate a VOD pipeline: transforms and jobs in a full publishing workflow. * Streaming and publishing: publish the job output for playback. * Webhooks: replace status polling with events.