Tutorials
Build a news summary video from JSON
Turn reviewed story records into a 28-second news brief with source labels. Validate the JSON, check credits, render once and download the MP4 with Node.js.
By Mina RamzyPublished Updated
Turn three reviewed story records into one source-labelled news brief. This tutorial supplies a JSON design and a complete Node.js program that validates your inputs, estimates credits, renders a vertical MP4 and downloads the finished file.
Your editorial workflow owns the facts. Zvid fills the design and renders the video. Keeping those responsibilities separate lets you reuse the same layout without treating a successful render as approval to publish.
The news brief you will make
The Harbor desk sample is a silent, 28-second video at 720 × 1280 pixels. It contains a four-second opening, three seven-second story cards and a three-second ending. Each story shows a headline, short summary, source name and publication date.
The supplied source URLs use example.com to demonstrate the record format. They are not evidence of real news. No media upload, narration service or stored template is needed to run this sample.
1. Download the example and set your API key
You need Node.js 22 or newer, npm, and a Zvid API key from app.zvid.io/api-keys. Run the program on your computer or server so the key stays private.
Budget 28 render credits for the supplied composition, then check the current estimate. Validation does not consume credits. The program only submits a render when you run the explicit render command, and its default ceiling prevents submission if the estimate exceeds 28 credits.
Extract the ZIP and open a terminal in its news-summary-video-template-json folder, where package.json is located. Check Node.js and install the project:
node --version
npm ci
Copy .env.example to a file named .env in that folder. On Windows PowerShell:
Copy-Item .env.example .env
On macOS or Linux:
cp .env.example .env
Open .env in a text editor and replace the placeholder with your API key:
ZVID_API_KEY=zvid_replace_with_your_key
MAX_RENDER_CREDITS=28
The commands load this file automatically. Keep the key out of project JSON and source control. The client sends it in the x-api-key header to https://api.zvid.io; the media download does not receive your key. The authentication guide explains key management.
If PowerShell blocks npm.ps1, use npm.cmd in place of npm throughout these commands.
2. Understand the story input
Run the supplied stories unchanged first. The files have separate jobs:
| File | Purpose |
|---|---|
data/stories.json |
The edition name, edition date and three story records. |
data/template.json |
The complete design, including text positions, colors and scene durations. |
src/stories.js |
Input checks, display-label mapping and checks of the resolved composition. |
src/index.js |
Validation, deliberate submission, job tracking and downloading. |
Here is the first record inside stories:
{
"id": "library-hours",
"tag": "LIBRARY",
"title": "Library adds Saturday hours",
"summary": "The reading room opens from 10 AM to 2 PM.",
"sourceName": "Library notice",
Collapse example
{
"id": "library-hours",
"tag": "LIBRARY",
"title": "Library adds Saturday hours",
"summary": "The reading room opens from 10 AM to 2 PM.",
"sourceName": "Library notice",
"sourceUrl": "https://example.com/library-hours",
"publishedAt": "2026-10-27",
"approved": true
}
The program assigns display number 01 from the record's position. It combines sourceName and publishedAt into Library notice / 27 OCT 2026, which appears beneath the summary. The source URL stays in the input record; the program does not fetch it or put it on screen.
This example deliberately accepts one to three stories, with short copy that fits its fixed vertical layout. Titles have a 48-character limit, summaries 76, tags 14 and source names 22. These are rules of the sample application, not universal Zvid limits.
Every story must have the boolean approved: true, a unique slug ID, a distinct HTTPS source URL without credentials, and a valid publication date no later than the edition date. The program also rejects unknown fields and markup or placeholder syntax in the text. An approval flag records a decision; it does not prove that a claim is true.
3. Resolve the design and inspect the estimate
Run the offline checks, then validate the sample through Zvid:
npm run check
npm test
npm start
The first two commands stay on your computer. npm start checks the records, sends the design and mapped variables to Zvid's validation endpoint, then checks the resolved text, source labels, scene order and duration. It does not create a render job.
For the supplied sample, expect:
5 scenes, 28 seconds. Estimate: 28 credits.
Saved out/resolved-project.json. No render submitted.
Open out/validation.json to read the current creditsRequired and any warnings. Open out/resolved-project.json and check these scenes in order:
| Scene | Duration | Content |
|---|---|---|
opening |
4 seconds | HARBOR DESK BRIEF and the edition date. |
story-1 |
7 seconds | Library hours and Library notice / 27 OCT 2026. |
story-2 |
7 seconds | Riverside path and Parks notice / 27 OCT 2026. |
story-3 |
7 seconds | Museum evening and Museum notice / 27 OCT 2026. |
closing |
3 seconds | A reminder to read the full notices. |
A resolved project is JSON, not a rendered preview image. Structural validation cannot guarantee readable layout or factual accuracy. The program stops on validation warnings so you can address them before spending render credits.
You can inspect the complete parameterized template and the resolved sample project separately. Both are project JSON files. A direct REST request wraps a project in a payload field, as the supplied program already does.
4. Render once, then save the finished MP4
When you have reviewed the inputs and estimate, run:
npm run render
This command validates again, checks MAX_RENDER_CREDITS, records a submission attempt and submits one paid render. It saves the returned job ID in out/run.json, polls for completion, and downloads the result to out/news-brief.mp4.
Rendering consumes credits. The estimate is checked at submission time, so a changed composition or account balance can affect whether the request succeeds. Read Zvid's render lifecycle for job states and output retention.
Play the downloaded file from beginning to end. Confirm that each title and summary fits, the source dates match the input, and all three cards remain on screen long enough to read. This design uses hard cuts and no audio. Put real source links in the caption or page beside a published video; text inside an MP4 is not clickable.
Recover an interrupted run
If polling times out or the download fails after the job ID was saved, run:
npm run resume
Resume checks the existing job and downloads its result. It does not submit another render. Completion depends on the job's state: "completed", not a progress percentage alone.
If submission stopped before the job ID was saved, first check Renders at app.zvid.io. A lost response does not establish that no job was created. Once you find its UUID, replace YOUR_JOB_ID and run:
npm run resume -- --job-id YOUR_JOB_ID
For a failed job, read failedReason in out/job.json and fix the cause before considering another paid render. For an expired output, inspect the job in the dashboard; resume cannot recreate deleted media. Keep the saved run records. To produce a new edition, use a fresh copy of the example so you retain the previous job ID.
How one story scene becomes three
The template contains one repeated scene with this configuration:
{
"id": "story",
"iterate": "stories",
"iterateAs": "story",
"duration": 7
}
This is an excerpt; the downloadable template includes its complete visuals array. Dynamic content repeats the scene for each array item and resolves fields such as {{story.title}} and {{story.sourceLine}}. The expanded IDs are story-1, story-2 and story-3.
With this layout, one story produces 14 seconds, two produce 21 seconds and three produce 28 seconds. If you change the durations in data/template.json, also update expectedTimeline in src/stories.js and its tests. The consistency check is intentional: it catches an unexpected timeline before submission.
Adapt the brief for a new edition
Change the edition name, date and reviewed records in data/stories.json. Keep the same field names and validate again. Reordering the array changes the story order and numbering; it does not change the layout.
For real reporting, replace the fictional stories with verified records and update the design's fictional labels only after that replacement. Keep the demo label while using the supplied sample. Preserve the full source URLs in your editorial records and choose your own freshness policy; this example does not reject an older story merely because it is old.
Review one completed render whenever you change typography, copy limits or durations. The program does not scrape news, summarize articles, fact-check claims, add narration, schedule updates or post videos. Those remain separate steps in your publishing workflow.
If you later add prepared narration, use the voiceover, captions and scene timing walkthrough to align its audio and text. Start by replacing one fictional record, validate the changed brief, then deliberately render and review the new edition.
Frequently asked questions
Does Zvid verify the news stories in a summary video?
No. Zvid resolves the supplied design and variables and renders the video. Your editorial workflow must verify claims, sources, dates and any media rights. The example's approved: true field is an application gate, not factual verification.
How many credits does this news summary example use?
The supplied 28-second composition was estimated at 28 render credits. Run npm start to get the current estimate without consuming credits, then use npm run render only when ready to spend them. The default MAX_RENDER_CREDITS=28 blocks a higher estimate.
What should I do if Zvid returns HTTP 401?
Check that .env contains a current API key copied from https://app.zvid.io/api-keys. Replace the placeholder after ZVID_API_KEY= and run the command again. The provided server-side client sends the key in the x-api-key header; it does not belong in your story records or template.
Can I resume a render without paying for a second video?
Yes. npm run resume tracks the saved job ID and downloads its output without submitting another render. If no ID was saved, check Renders in the dashboard and use npm run resume -- --job-id YOUR_JOB_ID with the actual UUID. An expired or failed job needs investigation before a new paid submission.
Can I include more than three stories or add a voiceover?
Yes, with changes to the example. Its one-to-three-story limit is an application choice for this short layout; Zvid applies your account's current limits during validation. More stories require revisiting the input cap, runtime checks and pacing. Narration requires an audio asset and coordinated timing, which this silent example does not supply.