{"id":39526,"date":"2025-12-04T07:00:00","date_gmt":"2025-12-04T15:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=39526"},"modified":"2025-12-04T09:44:27","modified_gmt":"2025-12-04T17:44:27","slug":"triggering-video-transformations-webhooks-api-calls","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls","title":{"rendered":"Triggering Video Transformations via Webhooks and API Calls"},"content":{"rendered":"\n<p>If you\u2019re only applying a single <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/documentation\/video_manipulation_and_delivery\" rel=\"noreferrer noopener\">transformation<\/a> to a short clip, manually processing may be fine. However, most post-production video workflows involve compressing, trimming, overlaying watermarks, and creating platform-specific variants.&nbsp;<\/p>\n\n\n\n<p>When each of these steps depends on a person or a scheduled task to trigger them, bottlenecks arise. Nobody knows which step is pending or next, and files get lost in the shuffle. An event-driven automation workflow eliminates bottlenecks by triggering each transformation as soon as the previous one finishes. Instead of polling for completion or manually kicking off new jobs, your pipeline responds automatically as events occur.<\/p>\n\n\n\n<p>In this post, we\u2019ll design an automated video processing pipeline using Cloudinary\u2019s <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/documentation\/eager_and_incoming_transformations#eager_asynchronous_transformations\" rel=\"noreferrer noopener\">eager asynchronous transformations<\/a> and <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/documentation\/notifications\" rel=\"noreferrer noopener\">webhook notifications<\/a>, so each stage in your workflow runs smoothly.<\/p>\n\n\n\n<p>Before we get into how these two features work together, let\u2019s start by understanding eager asynchronous transformations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Are Eager Asynchronous Transformations?<\/h2>\n\n\n\n<p>Eager transformations let you create specific versions of a video or image as soon as the file is uploaded. You define the transformations in the upload request, and Cloudinary processes them right away instead of waiting for someone to request them later.<\/p>\n\n\n\n<p>For example, with the <a target=\"_blank\" href=\"https:\/\/www.npmjs.com\/package\/cloudinary\" rel=\"noreferrer noopener\">Cloudinary Node.js SDK<\/a>, you can immediately generate a compressed MP4 variant and a thumbnail from a single video upload, as shown below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.uploader.upload(<span class=\"hljs-string\">'sample.mp4'<\/span>, {\n\n\u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager<\/span>: &#91;\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">'1200k'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'mp4'<\/span> }, <span class=\"hljs-comment\">\/\/ compressed variant<\/span>\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">320<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">180<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'jpg'<\/span> } <span class=\"hljs-comment\">\/\/ thumbnail<\/span>\n\n\u00a0 ]\n\n})\n\n.then(<span class=\"hljs-function\"><span class=\"hljs-params\">result<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Upload + eager transforms complete:'<\/span>, result))\n\n.catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>To try the code above, you\u2019ll need a <a href=\"https:\/\/cloudinary.com\/users\/register_free\">Cloudinary account<\/a> and the Node.js SDK installed. In your project, <a href=\"https:\/\/cloudinary.com\/documentation\/node_integration#landingpage\">include the SDK<\/a> and make sure your environment variables (CLOUDINARY_URL or <a href=\"https:\/\/cloudinary.com\/documentation\/finding_your_credentials_tutorial\">API credentials<\/a>) are set up.<\/p>\n<\/div>\n\n\n<p>By default, <strong>eager transformations are synchronous<\/strong>. That means in the code above, Cloudinary processes the transformations during the upload call, and your application waits until they\u2019re finished before getting a response.<\/p>\n\n\n\n<p>This setup is for lightweight transformations that you need immediately, but it can slow things down if the work is heavier or if you\u2019re processing multiple variants at the same time.<\/p>\n\n\n\n<p>To avoid that slowdown, you can run eager <strong>asynchronous transformations<\/strong> by setting <code>eager_async: true<\/code>:\u00a0<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.uploader.upload(<span class=\"hljs-string\">'sample.mp4'<\/span>, {\n\n\u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager<\/span>: &#91;\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">'1200k'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'mp4'<\/span> }, \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-comment\">\/\/ compressed<\/span>\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">320<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">180<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'jpg'<\/span> } <span class=\"hljs-comment\">\/\/ thumbnail<\/span>\n\n\u00a0 ],\n\n\u00a0 <span class=\"hljs-attr\">eager_async<\/span>: <span class=\"hljs-literal\">true<\/span>\n\n})\n\n.then(<span class=\"hljs-function\"><span class=\"hljs-params\">r<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Upload complete; transforms running in background:'<\/span>, r.public_id))\n\n.catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>eager_async: true<\/code> enables transformations to start right away, but they run in the background on Cloudinary\u2019s infrastructure. Your application gets a response as soon as the file is stored, while the defined eager transformations continue processing asynchronously.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Eager Transformation Notifications<\/h3>\n\n\n\n<p>For eager transformations, you can set a callback URL to notify your application when the transformation is complete. Once Cloudinary finishes processing, it will send a webhook to the URL you specify with details about the new assets.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.uploader.upload(<span class=\"hljs-string\">'sample.mp4'<\/span>, {\n\n\u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager<\/span>: &#91;\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">'1200k'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'mp4'<\/span> },\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">320<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">180<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'jpg'<\/span> }\n\n\u00a0 ],\n\n\u00a0 <span class=\"hljs-attr\">eager_async<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager_notification_url<\/span>: <span class=\"hljs-string\">'https:\/\/your-app.com\/webhooks\/eager-complete'<\/span>\n\n})\n\n.then(<span class=\"hljs-function\"><span class=\"hljs-params\">r<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Waiting for webhook:'<\/span>, r.public_id))\n\n.catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>From there, you can automatically trigger the next step in your workflow, such as overlaying captions, running moderation, or delivering the processed files to their final destination.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Automate Video Transformations With Async Eager Transforms and Webhooks<\/h2>\n\n\n\n<p>Below are different scenarios in which you can apply an event-driven transformation workflow to build multi-step video processing pipelines.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Scenario 1: Chain Video Transformations and Publish to CMS<\/h3>\n\n\n\n<p>Let\u2019s say you want to build a process that <strong>compresses a video, creates a thumbnail, and then adds captions<\/strong> before publication.<\/p>\n\n\n\n<p>Start by uploading the video with eager transformations for compression and thumbnail generation. Then, set the <code>eager_notification_url<\/code> so Cloudinary can send a webhook to your application when those transformations are complete:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ chain-video-transformation\/index.js<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">v2<\/span>: cloudinary } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>);\n\ncloudinary.config({\n\n\u00a0 <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_CLOUD&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_KEY&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_SECRET&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">secure<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n});\n\ncloudinary.uploader.upload(<span class=\"hljs-string\">\"sample.mp4\"<\/span>, {\n\n\u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager<\/span>: &#91;\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>, <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">\"1200k\"<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"mp4\"<\/span> },\n\n\u00a0 \u00a0 { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">320<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">180<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"fill\"<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"jpg\"<\/span> },\n\n\u00a0 ],\n\n\u00a0 <span class=\"hljs-attr\">eager_async<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">eager_notification_url<\/span>: <span class=\"hljs-string\">\"https:\/\/your-app.com\/webhooks\/eager-complete\"<\/span>,\n\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>By implementing the code above, you can automate the process of uploading a video, applying compression, generating a thumbnail, and receiving a webhook notification at <code>your-app.com\/webhooks\/eager-complete<\/code> once all tasks are complete.<\/p>\n\n\n\n<p>Next, you can create a webhook endpoint using Express, (lightweight web framework for Node.js) to handle the incoming notification and trigger the next step of adding captions to the compressed video.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ chain-video-transformation\/server.js<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> express = <span class=\"hljs-keyword\">require<\/span>(<span class=\"hljs-string\">\"express\"<\/span>);\n\n<span class=\"hljs-keyword\">const<\/span> { v2: cloudinary } = <span class=\"hljs-keyword\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>);\n\ncloudinary.config({\n\n\u00a0 cloud_name: <span class=\"hljs-string\">\"&lt;YOUR_CLD_CLOUD&gt;\"<\/span>,\n\n\u00a0 api_key: <span class=\"hljs-string\">\"&lt;YOUR_CLD_KEY&gt;\"<\/span>,\n\n\u00a0 api_secret: <span class=\"hljs-string\">\"&lt;YOUR_CLD_SECRET&gt;\"<\/span>,\n\n\u00a0 secure: <span class=\"hljs-keyword\">true<\/span>,\n\n});\n\n<span class=\"hljs-keyword\">const<\/span> app = express();\n\napp.<span class=\"hljs-keyword\">use<\/span>(<span class=\"hljs-title\">express<\/span>.<span class=\"hljs-title\">json<\/span>());\n\napp.post(<span class=\"hljs-string\">\"\/webhooks\/eager-complete\"<\/span>, async (req, res) =&gt; {\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> publicId = req.body.public_id;\n\n\u00a0 await cloudinary.uploader.explicit(publicId, {\n\n\u00a0 \u00a0 resource_type: <span class=\"hljs-string\">\"video\"<\/span>,\n\n\u00a0 \u00a0 type: <span class=\"hljs-string\">\"upload\"<\/span>,\n\n\u00a0 \u00a0 eager: &#91;\n\n\u00a0 \u00a0 \u00a0 {\n\n\u00a0 \u00a0 \u00a0 \u00a0 overlay: {\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 resource_type: <span class=\"hljs-string\">\"subtitles\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 public_id: <span class=\"hljs-string\">\"captions\/myfile.vtt\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 },\n\n\u00a0 \u00a0 \u00a0 \u00a0 flags: <span class=\"hljs-string\">\"layer_apply\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 quality: <span class=\"hljs-string\">\"auto\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 bit_rate: <span class=\"hljs-string\">\"1200k\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 format: <span class=\"hljs-string\">\"mp4\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 },\n\n\u00a0 \u00a0 ],\n\n\u00a0 \u00a0 eager_async: <span class=\"hljs-keyword\">true<\/span>,\n\n\u00a0 \u00a0 eager_notification_url: <span class=\"hljs-string\">\"https:\/\/your-app.com\/webhooks\/final-video\"<\/span>,\n\n\u00a0 });\n\n\u00a0 res.sendStatus(<span class=\"hljs-number\">200<\/span>);\n\n});\n\napp.listen(<span class=\"hljs-number\">9090<\/span>, () =&gt; {\n\n\u00a0 console.log(`Server is running on port http:<span class=\"hljs-comment\">\/\/localhost:9090`);<\/span>\n\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The webhook above listens for <a href=\"https:\/\/cloudinary.com\/documentation\/notifications\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary\u2019s notification<\/a> when the first set of transformations is complete, then runs an additional transformation to overlay captions on the processed video. Once that step finishes, it triggers another webhook notification to <code>your-app.com\/webhooks\/final-video<\/code> for final processing.<\/p>\n\n\n\n<p>Finally, define a second webhook endpoint that pushes the fully processed video to your CMS or delivery queue:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">app.post(<span class=\"hljs-string\">\"\/webhooks\/final-video\"<\/span>, (req, res) =&gt; {\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> derivedAssets = req.body.derived || &#91;];\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> finalUrl = req.body.eager&#91;<span class=\"hljs-number\">0<\/span>]?.url;\n\n\u00a0 <span class=\"hljs-keyword\">if<\/span> (finalUrl) {\n\n\u00a0 \u00a0 <span class=\"hljs-comment\">\/\/ Push finalUrl to CMS, publish to queue, etc.<\/span>\n\n\u00a0 }\n\n\u00a0 res.sendStatus(<span class=\"hljs-number\">200<\/span>);\n\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You can find the complete code for this example in this <a href=\"https:\/\/github.com\/olawanlejoel\/eager-transformation-cloudinary\/tree\/main\/chain-video-transformation\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub repository<\/a>.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Scenario 2: Bulk Video Ingestion With Transcoding and Moderation<\/h3>\n\n\n\n<p>Suppose you need to <strong>import a large video catalog, convert each file into delivery-ready formats, and validate each video<\/strong> (e.g., ensuring it meets specific duration or resolution requirements) before adding it to your platform.<\/p>\n\n\n\n<p>Start by <code>batch-uploading<\/code> each file to Cloudinary with eager asynchronous transformations that generate HD, SD, and a preview thumbnail in the background.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ bulk-ingestion-with-transcoding\/index.js<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">v2<\/span>: cloudinary } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>);\n\ncloudinary.config({\n\n\u00a0 <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_CLOUD&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_KEY&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_SECRET&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">secure<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n});\n\n<span class=\"hljs-keyword\">const<\/span> sources = &#91;\n\n\u00a0 <span class=\"hljs-string\">\"path\/to\/sample.mp4\"<\/span>,\n\n\u00a0 <span class=\"hljs-string\">\"path\/to\/sample2.mp4\"<\/span>,\n\n\u00a0 <span class=\"hljs-string\">\"path\/to\/sample3.mp4\"<\/span>,\n\n];\n\n<span class=\"hljs-keyword\">const<\/span> eagerPresets = &#91;\n\n\u00a0 {\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">1280<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">720<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"limit\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">\"2500k\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"mp4\"<\/span>,\n\n\u00a0 }, <span class=\"hljs-comment\">\/\/ HD<\/span>\n\n\u00a0 {\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">854<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">480<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"limit\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">bit_rate<\/span>: <span class=\"hljs-string\">\"1200k\"<\/span>,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"mp4\"<\/span>,\n\n\u00a0 }, <span class=\"hljs-comment\">\/\/ SD<\/span>\n\n\u00a0 { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">320<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">180<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"fill\"<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"jpg\"<\/span> }, <span class=\"hljs-comment\">\/\/ thumbnail<\/span>\n\n];\n\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">uploadBatch<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> src <span class=\"hljs-keyword\">of<\/span> sources) {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(src, {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">folder<\/span>: <span class=\"hljs-string\">\"ingest\/bulk\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">eager<\/span>: eagerPresets,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">eager_async<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">eager_notification_url<\/span>: <span class=\"hljs-string\">\"https:\/\/your-app.com\/webhooks\/bulk-eager\"<\/span>,\n\n\u00a0 \u00a0 });\n\n\u00a0 }\n\n}\n\nuploadBatch().catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This code uploads each video in the sources list, creates three derived versions (HD, SD, and a thumbnail), and sets an <code>eager_notification_url<\/code> so Cloudinary will send a webhook to your endpoint once all transformations are complete.<\/p>\n\n\n\n<p>To handle the webhook callback, you\u2019ll need a small web server capable of receiving HTTP POST requests from Cloudinary, such as Express.<\/p>\n\n\n\n<p>In the example below, you\u2019ll define an endpoint (\/webhooks\/bulk-eager) that Cloudinary will call once all eager transformations are finished. Inside this handler, we\u2019ll validate each video, tag it accordingly, and log the result.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ bulk-ingestion-with-transcoding\/server.js<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> express = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"express\"<\/span>);\n\n<span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">v2<\/span>: cloudinary } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>);\n\ncloudinary.config({\n\n\u00a0 <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_CLOUD&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_KEY&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">\"&lt;YOUR_CLD_SECRET&gt;\"<\/span>,\n\n\u00a0 <span class=\"hljs-attr\">secure<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n});\n\n<span class=\"hljs-keyword\">const<\/span> app = express();\n\n<span class=\"hljs-keyword\">const<\/span> runModerationCheck = <span class=\"hljs-keyword\">async<\/span> (publicId, derived) =&gt; {\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> video = <span class=\"hljs-function\">(<span class=\"hljs-params\">derived || &#91;]<\/span>).<span class=\"hljs-params\">find<\/span>(<span class=\"hljs-params\">(d<\/span>) =&gt;<\/span> d.format === <span class=\"hljs-string\">\"mp4\"<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> duration = video?.duration || <span class=\"hljs-number\">0<\/span>;\n\n\u00a0 <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Moderation check for <span class=\"hljs-subst\">${publicId}<\/span>: duration <span class=\"hljs-subst\">${duration}<\/span>s`<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">return<\/span> duration &gt;= <span class=\"hljs-number\">5<\/span> &amp;&amp; duration &lt;= <span class=\"hljs-number\">600<\/span>;\n\n};\n\napp.post(<span class=\"hljs-string\">\"\/webhooks\/bulk-eager\"<\/span>, <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> event = req.body;\n\n\u00a0 <span class=\"hljs-keyword\">if<\/span> (event.notification_type !== <span class=\"hljs-string\">\"eager\"<\/span> || event.status !== <span class=\"hljs-string\">\"success\"<\/span>)\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> res.sendStatus(<span class=\"hljs-number\">200<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">public_id<\/span>: publicId, derived = &#91;] } = event;\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> approved = <span class=\"hljs-keyword\">await<\/span> runModerationCheck(publicId, derived);\n\n\u00a0 <span class=\"hljs-keyword\">if<\/span> (!approved) {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.add_tag(&#91;<span class=\"hljs-string\">\"quarantine\"<\/span>], &#91;publicId], {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span>,\n\n\u00a0 \u00a0 });\n\n\u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Quarantined:\"<\/span>, publicId);\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> res.sendStatus(<span class=\"hljs-number\">200<\/span>);\n\n\u00a0 }\n\n\u00a0 <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.add_tag(&#91;<span class=\"hljs-string\">\"approved\"<\/span>], &#91;publicId], {\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span>,\n\n\u00a0 });\n\n\u00a0 <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Approved:\"<\/span>, publicId);\n\n\u00a0 <span class=\"hljs-comment\">\/\/ do something with the approved video<\/span>\n\n\u00a0 res.sendStatus(<span class=\"hljs-number\">200<\/span>);\n\n});\n\napp.listen(<span class=\"hljs-number\">9090<\/span>, () =&gt; {\n\n\u00a0 <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Server is running on port http:\/\/localhost:9090`<\/span>);\n\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The code above defines a <code>runModerationCheck()<\/code> function that approves videos only if their duration is between 6 and 500 seconds. In the webhook handler, this check runs whenever the transformations from the previous step are complete.\u00a0<\/p>\n\n\n\n<p>Videos that fail are tagged as quarantine; those that pass are tagged as approved. Additionally, for approved videos, the handler retrieves the HD, SD, and thumbnail URLs from the derived list and logs them, ready for publishing.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You can find the complete <a href=\"https:\/\/github.com\/olawanlejoel\/eager-transformation-cloudinary\/tree\/main\/bulk-ingestion-with-transcoding\" target=\"_blank\" rel=\"noreferrer noopener\">code on GitHub<\/a>.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Benefits<\/h2>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>No polling or manual triggers.<\/strong> Each transformation stage starts automatically as soon as the previous one finishes, so you don\u2019t have to make constant status checks.<\/li>\n\n\n\n<li><strong>Asynchronous background processing.<\/strong> Cloudinary handles all heavy compute operations behind the scenes, keeping your application responsive while assets are processed.<\/li>\n\n\n\n<li><strong>Scalable chaining.<\/strong> Webhooks enable you to chain multiple transformation steps, such as compression, captioning, and publishing in a reliable, event-driven sequence.<\/li>\n\n\n\n<li><strong>Lower latency and fewer errors.<\/strong> Automation removes repetitive manual actions, reducing human error and accelerating your content pipeline from upload to delivery.<\/li>\n\n\n\n<li><strong>Improved cost and quota control.<\/strong> By batching and grouping transformations, you can better manage API calls and processing usage, leading to more predictable billing and performance.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Best Practices for Automating Video Transformation Workflows<\/h2>\n\n\n\n<p>These best practices can help ensure your automated video pipeline runs efficiently and at scale:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Verify webhook authenticity.<\/strong> Always validate incoming webhooks using Cloudinary\u2019s timestamp and signature headers before processing them. This ensures the request originated from Cloudinary and protects your endpoints from spoofed or malicious calls.<\/li>\n\n\n\n<li><strong>Handle retries gracefully.<\/strong> Cloudinary automatically retries failed webhook deliveries up to three times. Make your webhook handlers idempotent and resilient, so duplicate notifications don\u2019t cause repeated transformations or conflicting updates.<\/li>\n\n\n\n<li><strong>Use separate endpoints for upload and eager events.<\/strong> Differentiate between upload completion events and eager transformation notifications by routing them to distinct webhook endpoints. This separation keeps your logic modular and prevents accidental cross-triggering of unrelated actions.<\/li>\n\n\n\n<li><strong>Track transformation chains<\/strong>. Use transformation grouping or derived asset trees to visualize and manage your processing pipelines. This helps track which assets were generated from a source file, making debugging and reporting much easier in multi-step workflows.<\/li>\n\n\n\n<li><strong>Avoid redundant transformations.<\/strong> Leverage caching and transformation tracking to prevent reprocessing identical variants. Before launching a new transformation, check whether an equivalent derived asset already exists to save compute resources and API quota.<\/li>\n\n\n\n<li><strong>Monitor and measure pipeline health.<\/strong> Track event timings, error rates, and transformation counts to identify bottlenecks early. Monitoring how long each stage takes helps you detect slowdowns, failed callbacks, or retry loops before they affect publishing or delivery.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts&nbsp;<\/h2>\n\n\n\n<p>Automatically trigger each step the moment the last one finishes with Cloudinary\u2019s eager asynchronous transformations and webhook notifications. By using an event-driven transformation workflow, you can build smarter video pipelines that moderates content, tailors output for different platforms, and routes assets based on business rules.<\/p>\n\n\n\n<p>Ready to automate your video workflow? Sign up for a <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/users\/register\/free\" rel=\"noreferrer noopener\">free Cloudinary account<\/a> and build your own event-driven video pipelines with eager transformations and webhooks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you\u2019re only applying a single transformation to a short clip, manually processing may be fine. However, most post-production video workflows involve compressing, trimming, overlaying watermarks, and creating platform-specific variants.&nbsp; When each of these steps depends on a person or a scheduled task to trigger them, bottlenecks arise. Nobody knows which step is pending or [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":39527,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[305,304],"class_list":["post-39526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-video-api","tag-video-transformation"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Triggering Video Transformations via Webhooks and API Calls<\/title>\n<meta name=\"description\" content=\"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Triggering Video Transformations via Webhooks and API Calls\" \/>\n<meta property=\"og:description\" content=\"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-12-04T15:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-12-04T17:44:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Triggering Video Transformations via Webhooks and API Calls\",\"datePublished\":\"2025-12-04T15:00:00+00:00\",\"dateModified\":\"2025-12-04T17:44:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\"},\"wordCount\":1290,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA\",\"keywords\":[\"Video API\",\"Video Transformation\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\",\"url\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\",\"name\":\"Triggering Video Transformations via Webhooks and API Calls\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA\",\"datePublished\":\"2025-12-04T15:00:00+00:00\",\"dateModified\":\"2025-12-04T17:44:27+00:00\",\"description\":\"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Triggering Video Transformations via Webhooks and API Calls\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Triggering Video Transformations via Webhooks and API Calls","description":"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls","og_locale":"en_US","og_type":"article","og_title":"Triggering Video Transformations via Webhooks and API Calls","og_description":"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.","og_url":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls","og_site_name":"Cloudinary Blog","article_published_time":"2025-12-04T15:00:00+00:00","article_modified_time":"2025-12-04T17:44:27+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Triggering Video Transformations via Webhooks and API Calls","datePublished":"2025-12-04T15:00:00+00:00","dateModified":"2025-12-04T17:44:27+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls"},"wordCount":1290,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","keywords":["Video API","Video Transformation"],"inLanguage":"en-US","copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls","url":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls","name":"Triggering Video Transformations via Webhooks and API Calls","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","datePublished":"2025-12-04T15:00:00+00:00","dateModified":"2025-12-04T17:44:27+00:00","description":"Manual video workflows slow teams down. Cloudinary automates video transformations with webhooks and async processing to optimize post-upload steps.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/triggering-video-transformations-webhooks-api-calls#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Triggering Video Transformations via Webhooks and API Calls"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1763684063\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls\/Blog_Triggering_Video_Transformations_via_Webhooks_and_API_Calls.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=39526"}],"version-history":[{"count":2,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39526\/revisions"}],"predecessor-version":[{"id":39573,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39526\/revisions\/39573"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/39527"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=39526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=39526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=39526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}