<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Founder Flow</title>
    <description>The latest articles on DEV Community by Founder Flow (@rendofy).</description>
    <link>https://dev.to/rendofy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4113438%2Fd20aba78-010e-4bbe-9387-01bdfa1b22a3.png</url>
      <title>DEV Community: Founder Flow</title>
      <link>https://dev.to/rendofy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rendofy"/>
    <language>en</language>
    <item>
      <title>How to Turn JSON Into Video With a Webhook API</title>
      <dc:creator>Founder Flow</dc:creator>
      <pubDate>Mon, 07 Sep 2026 07:57:55 +0000</pubDate>
      <link>https://dev.to/rendofy/how-to-turn-json-into-video-with-a-webhook-api-1d19</link>
      <guid>https://dev.to/rendofy/how-to-turn-json-into-video-with-a-webhook-api-1d19</guid>
      <description>&lt;p&gt;I've been working on Rendofy, a video rendering API for automation workflows, and one thing became obvious pretty quickly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rendering the video isn't really the hard part. Making rendering behave nicely inside an automation is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A render might take several seconds (or longer), so keeping an HTTP request open until everything finishes isn't ideal. Polling the API every few seconds works, but now you've added more requests, more workflow executions and more state to keep track of.&lt;/p&gt;

&lt;p&gt;What I really wanted was something much simpler:&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
Send JSON&lt;br&gt;
   ↓&lt;br&gt;
Start render&lt;br&gt;
   ↓&lt;br&gt;
Go do something else&lt;br&gt;
   ↓&lt;br&gt;
Get a webhook when the MP4 is ready'''&lt;/p&gt;

&lt;p&gt;That's the approach I ended up using with Rendofy.&lt;/p&gt;

&lt;p&gt;Here's how it works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start with structured data
&lt;/h2&gt;

&lt;p&gt;Instead of sending a video file, the workflow starts with ordinary JSON.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;'''json&lt;br&gt;
{&lt;br&gt;
  "quote": "Ship the boring version first.",&lt;br&gt;
  "author": "Rendofy"&lt;br&gt;
}'''&lt;/p&gt;

&lt;p&gt;Nothing particularly interesting there, and that's kind of the point.&lt;/p&gt;

&lt;p&gt;That JSON could have come from an RSS feed, database, CMS, Google Sheet, AI workflow or another API. The rendering step doesn't really care where it came from.&lt;/p&gt;

&lt;p&gt;The application just needs to turn that data into a video.&lt;/p&gt;
&lt;h2&gt;
  
  
  Send the render request
&lt;/h2&gt;

&lt;p&gt;Rendofy accepts the render request through an HTTP endpoint:&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
POST &lt;a href="https://api.rendofy.com/webhook/render-intake''" rel="noopener noreferrer"&gt;https://api.rendofy.com/webhook/render-intake''&lt;/a&gt;'&lt;/p&gt;

&lt;p&gt;A basic request looks like this:&lt;/p&gt;

&lt;p&gt;'''bash&lt;br&gt;
curl -X POST \&lt;br&gt;
  &lt;a href="https://api.rendofy.com/webhook/render-intake" rel="noopener noreferrer"&gt;https://api.rendofy.com/webhook/render-intake&lt;/a&gt; \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{&lt;br&gt;
    "api_key": "YOUR_API_KEY",&lt;br&gt;
    "callback_url": "&lt;a href="https://your-app.example/webhooks/render" rel="noopener noreferrer"&gt;https://your-app.example/webhooks/render&lt;/a&gt;",&lt;br&gt;
    "payload": {&lt;br&gt;
      "quote": "Ship the boring version first.",&lt;br&gt;
      "author": "Rendofy"&lt;br&gt;
    }&lt;br&gt;
  }'''&lt;/p&gt;

&lt;p&gt;In a real workflow I'd obviously keep the API key in the platform's credential store or an environment variable rather than putting it directly into the workflow.&lt;/p&gt;

&lt;p&gt;The important piece here is &lt;code&gt;callback_url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's where the finished result will come back.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't wait for the video
&lt;/h2&gt;

&lt;p&gt;The API doesn't make the original request sit around while the renderer does its work.&lt;/p&gt;

&lt;p&gt;It responds immediately with something like:&lt;/p&gt;

&lt;p&gt;'''json&lt;br&gt;
{&lt;br&gt;
  "status": "queued",&lt;br&gt;
  "job_id": "YOUR_JOB_ID"&lt;br&gt;
}'''&lt;/p&gt;

&lt;p&gt;I initially found this separation really useful when testing the system because it makes it very clear what &lt;code&gt;queued&lt;/code&gt; actually means.&lt;/p&gt;

&lt;p&gt;It doesn't mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;your video is ready.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I accepted your render job. I'll get back to you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The caller can move on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Then the webhook arrives
&lt;/h2&gt;

&lt;p&gt;Once the render finishes, Rendofy POSTs the result to the callback URL supplied earlier.&lt;/p&gt;

&lt;p&gt;A successful result contains the completed job information and the MP4 URL.&lt;/p&gt;

&lt;p&gt;Conceptually, it looks like:&lt;/p&gt;

&lt;p&gt;'''json&lt;br&gt;
{&lt;br&gt;
  "status": "completed",&lt;br&gt;
  "job_id": "YOUR_JOB_ID",&lt;br&gt;
  "video_url": "&lt;a href="https://cdn.example.com/rendered-video.mp4" rel="noopener noreferrer"&gt;https://cdn.example.com/rendered-video.mp4&lt;/a&gt;"&lt;br&gt;
}'''&lt;/p&gt;

&lt;p&gt;At that point the automation has an actual video it can do something with.&lt;/p&gt;

&lt;p&gt;Upload it somewhere. Store it. Pass it to another service. Continue another workflow. Whatever makes sense.&lt;/p&gt;

&lt;p&gt;So the complete flow becomes:&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
Content/data&lt;br&gt;
     ↓&lt;br&gt;
Build JSON&lt;br&gt;
     ↓&lt;br&gt;
POST render request&lt;br&gt;
     ↓&lt;br&gt;
Job queued&lt;br&gt;
     .&lt;br&gt;
     . rendering happens&lt;br&gt;
     .&lt;br&gt;
     ↓&lt;br&gt;
Webhook received&lt;br&gt;
     ↓&lt;br&gt;
MP4 URL&lt;br&gt;
     ↓&lt;br&gt;
Next step'''&lt;/p&gt;
&lt;h2&gt;
  
  
  Why I preferred this over polling
&lt;/h2&gt;

&lt;p&gt;Polling would have been easier to explain at first.&lt;/p&gt;

&lt;p&gt;Submit the render, wait five seconds, check the status, wait again, check again...&lt;/p&gt;

&lt;p&gt;But it felt wrong for the kind of workflows I was trying to support.&lt;/p&gt;

&lt;p&gt;If 100 renders are running, I don't want 100 workflows repeatedly asking the rendering service whether they're finished.&lt;/p&gt;

&lt;p&gt;I'd rather have the renderer say:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I'm done. Here's your result."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It also maps surprisingly well to tools like n8n and Make because webhooks are already a normal part of how those platforms work.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this looks like in n8n
&lt;/h2&gt;

&lt;p&gt;For example, you could have one side of an n8n workflow create the render:&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
RSS / CMS / API&lt;br&gt;
      ↓&lt;br&gt;
Prepare content&lt;br&gt;
      ↓&lt;br&gt;
Create render'''&lt;/p&gt;

&lt;p&gt;Then continue when the callback arrives:&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
Webhook&lt;br&gt;
   ↓&lt;br&gt;
Receive completed render&lt;br&gt;
   ↓&lt;br&gt;
Use MP4 URL&lt;br&gt;
   ↓&lt;br&gt;
Next action'''&lt;/p&gt;

&lt;p&gt;I've been testing this pattern quite a bit while building the Rendofy n8n integration.&lt;/p&gt;

&lt;p&gt;It also means the source of the content is completely separate from rendering.&lt;/p&gt;

&lt;p&gt;You could swap an RSS feed for Airtable tomorrow and the rendering architecture wouldn't need to change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Make works the same way
&lt;/h2&gt;

&lt;p&gt;I recently built the same integration for Make, and the architecture ended up being almost identical.&lt;/p&gt;

&lt;p&gt;'''text&lt;br&gt;
Content source&lt;br&gt;
      ↓&lt;br&gt;
Rendofy: Create a Render&lt;br&gt;
      ↓&lt;br&gt;
      .&lt;br&gt;
   rendering&lt;br&gt;
      .&lt;br&gt;
      ↓&lt;br&gt;
Custom Webhook&lt;br&gt;
      ↓&lt;br&gt;
Continue scenario'''&lt;/p&gt;

&lt;p&gt;That was actually a nice validation of the API design.&lt;/p&gt;

&lt;p&gt;If the same asynchronous pattern fits naturally into two different automation platforms without special handling, the abstraction is probably doing its job.&lt;/p&gt;
&lt;h2&gt;
  
  
  One thing I learned while testing this
&lt;/h2&gt;

&lt;p&gt;Error handling matters more than I expected.&lt;/p&gt;

&lt;p&gt;There's an important difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"We couldn't accept your request."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"We accepted your request, but the render later failed."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those need to be treated differently.&lt;/p&gt;

&lt;p&gt;For example, an invalid API key should fail immediately. A bad callback URL should also be rejected before creating a render job.&lt;/p&gt;

&lt;p&gt;But something that fails during the actual rendering process happens &lt;em&gt;after&lt;/em&gt; the job has been accepted, so that result needs to come through the asynchronous path.&lt;/p&gt;

&lt;p&gt;I ended up thinking about the lifecycle as three possibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request → rejected

Request → queued → completed

Request → queued → failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, but having that distinction made debugging the integration much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'm taking this
&lt;/h2&gt;

&lt;p&gt;The bigger idea behind Rendofy is that I don't want video rendering to be a separate manual workflow.&lt;/p&gt;

&lt;p&gt;I want it to behave like infrastructure.&lt;/p&gt;

&lt;p&gt;If an automation already has the data needed to make a video, it should be able to send that data somewhere and get an MP4 back without someone opening an editor.&lt;/p&gt;

&lt;p&gt;That opens up some interesting workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RSS → video&lt;/li&gt;
&lt;li&gt;CMS → video&lt;/li&gt;
&lt;li&gt;automated quote videos&lt;/li&gt;
&lt;li&gt;data-driven social content&lt;/li&gt;
&lt;li&gt;personalized video generation&lt;/li&gt;
&lt;li&gt;scheduled video pipelines&lt;/li&gt;
&lt;li&gt;AI workflow → structured JSON → video&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm building &lt;a href="https://rendofy.com" rel="noopener noreferrer"&gt;Rendofy&lt;/a&gt; around this model.&lt;/p&gt;

&lt;p&gt;I've also put the API quickstarts, webhook examples and JavaScript/Python/cURL examples in the &lt;a href="https://github.com/Rendofy/rendofy-developer" rel="noopener noreferrer"&gt;Rendofy developer repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm still building out the integrations, so I'm especially interested in hearing from people using n8n or Make:&lt;/p&gt;

&lt;p&gt;What would you actually automate if video rendering were just another node in your workflow?&lt;/p&gt;

</description>
      <category>webhook</category>
      <category>api</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
