Zvid
Editor
Pricing

Developer Tutorials

Video Template Validation: Test Input States Before Rendering

Run a complete Node.js example to check template variables, scene order, timing and failures, then render and review one release-status video.

Mina RamzyBy Mina RamzyPublished Updated

A template can produce valid JSON and still show the wrong title, keep an unwanted call to action, or turn an empty list into an awkward video. Test the data and the resolved sequence before you spend credits, then review the actual video for visual problems.

The Node.js example below checks six valid input states and six deliberate failures against one release-status template. Its default command stops after validation. A separate command renders one representative video and downloads it.

See what changes when the input changes

Watch the same release-status template resolve to four, three, two, or just one scene as its inputs change. Emptying the list or switching the call to action (CTA) off changes the result. This 48-second walkthrough also shows a rejected input type and the checks to run before reviewing an actual render.

Input changes lead to different scene sequences. The diagrams show the resolved project; the final segment plays the actual representative output. Narration explains each step.
Read the video transcript

One good-looking render does not prove that every input works.

Start with the same release-status template: a title, two items, and a call to action. The usual input resolves to four scenes.

Empty the list: the status scenes disappear. Turn the call to action off as well: only the intro remains.

Keep the items, but switch the call to action off: three scenes remain.

Now pass the word false as a string. Our input check rejects it. The Boolean false is the value we want.

Check the resolved text, scene order, and duration before rendering. These checks use no render credits.

Then watch an actual render. Valid structure still needs visual review.

The runnable example below produces the release-status video shown at the end of this walkthrough.

There are three different questions to answer:

  1. 1. Check the inputDoes the data meet your application's rules?Types, names, boundaries
  2. 2. Inspect the projectDoes Zvid resolve the intended content and sequence?Values, scenes, duration
  3. 3. Review the videoIs the finished result readable and well timed?Wrapping, contrast, transitions
A successful structural check does not approve the appearance of a video.

Run the checks without rendering

You need Node.js 22 or newer with npm, a Zvid account, and a key from app.zvid.io/api-keys. Run this on your computer or server, where the key can stay private. Your account's current limits must allow the sample's 1280 × 720 canvas and the matrix's longest duration of 13 seconds.

Validation and template preview do not consume render credits. Rendering the representative video is estimated at 11 render credits. Check the current estimate before submitting it; the included script refuses a render above its 11-credit ceiling.

The sample sends its template file inline as payload. It does not create a saved template in your account or need an existing template ID. Zvid resolves variables and placeholders for inline projects as well as stored templates.

1. Get the complete example

Extract the ZIP and open a terminal inside its video-template-validation folder, beside package.json. Run:

node --version
npm ci
npm run check
npm test

The ten tests run offline without credentials or credits. In PowerShell, use npm.cmd instead of npm if your execution policy blocks npm.ps1.

File What you will use it for
data/template.json The complete design, default variables, repeated status scene, and conditional CTA
data/cases.json Named input states and their expected results
src/preflight.js Input rules and checks for resolved content, scene order, count, and duration
src/index.js Validation by default, plus explicit render and resume commands
out/ Your validation responses, matrix summary, and any saved render job and video

2. Add your API key

Copy .env.example to .env in the same folder. In PowerShell, run Copy-Item .env.example .env. On macOS or Linux, run cp .env.example .env.

Open .env in a text editor, paste your key after ZVID_API_KEY=, and keep the credit ceiling:

ZVID_API_KEY=YOUR_API_KEY
MAX_RENDER_CREDITS=11

The npm commands load this file. Keep it out of source control, shared downloads, and browser code. The authentication guide explains API-key management.

3. Run the matrix

npm start

The script sends each valid case to POST https://api.zvid.io/api/render/validate/api-key. This endpoint resolves the template data and validates the resulting project against the current account limits. It returns the resolved payload, warnings, and creditsRequired, with no render job.

Expect a PASS line for every case, followed by:

Representative render estimate: 11 credits.
Matrix passed. No render submitted.

Open out/matrix.json for the summary. Open out/representative-validation.json to inspect the actual resolved project and estimate. A later run replaces the validation files, so copy them first if you want to compare two designs.

The script exits with an error if a valid case fails, a deliberately bad case is unexpectedly accepted, or a response does not match the expected content and structure. An authentication or network failure never counts as a successful negative test.

What the input states are testing

Each fixture is a named set of inputs with an expected outcome. This is the representative set from data/cases.json:

{
  "title": "October release status",
  "items": [
    { "label": "API", "value": "Ready" },
    { "label": "Editor", "value": "Ready" }
  ],
Show full example · 9 linesCollapse example
{
  "title": "October release status",
  "items": [
    { "label": "API", "value": "Ready" },
    { "label": "Editor", "value": "Ready" }
  ],
  "showCta": true,
  "cta": "Review the final render"
}

The design uses {{title}} for the introduction. Its status scene has "iterate": "items" and "iterateAs": "item", so each item becomes a scene containing {{item.label}} and {{item.value}}. The final scene uses "condition": "{{showCta}}". A Boolean false removes that scene.

These are iteration and condition rules, not an expression language. Calculate business decisions in your application, then pass the resulting Boolean. Use false, not the string "false".

Valid case Change from the representative input Expected sequence
Representative Two items, CTA on 4 scenes, 10.5 seconds
Long copy Longer title, labels, and status values 4 scenes, 10.5 seconds
Empty items items: [], CTA still on Intro and CTA only: 2 scenes, 5.5 seconds
CTA off Two items, showCta: false Intro and two statuses: 3 scenes, 8 seconds
Minimum Empty items and CTA off Intro only: 1 scene, 3 seconds
Maximum items Three items, CTA on 5 scenes, 13 seconds

The longest-copy fixture checks that those strings resolve unchanged. It does not prove that they fit visually. Review the long-copy layout when approving or changing the design.

The sample's input rules allow at most three items, a 64-character title, 28-character labels, 20-character status values, and a 48-character CTA. They reject empty text, HTML, template braces, unknown fields, and a non-Boolean showCta. These are application choices to keep this example predictable, not Zvid-wide limits or universal typography rules.

Four fixtures must fail those local rules: a string Boolean, an unknown variable, four items, and a 65-character title. Two additional fixtures deliberately bypass the local checks to confirm Zvid's resolution errors:

API failure case Expected result
items is the string "API" HTTP 400 with a field path containing iterate
An item has label but no value HTTP 400 with a text-field error for the unresolved {{item.value}}

Their response details are saved in out/wrong-items-type-error.json and out/missing-item-value-error.json. Normal production inputs should pass your own rules first. The data-and-design contract example explains that separation in more detail.

Check the result, not just a valid flag

For each accepted case, the script checks the actual title, status text, CTA, scene order, count, and duration. Checking content matters: a template can still resolve successfully after a designer renames a placeholder and leaves a plausible default behind.

For example, this is the central sequence check from the supplied helper, with the input named variables:

const ids = [
  'intro',
  ...variables.items.map((_, i) => `status-${i + 1}`),
  ...(variables.showCta ? ['cta'] : []),
];
assert.deepEqual(project.scenes.map(scene => scene.id), ids);

Duration has its own expectation. With these explicit three-second scenes and half-second fades, use the sum of scene durations minus the actual overlaps. Four scenes produce 12 - 1.5 = 10.5 seconds. The scene timing guide explains why transitions shorten the combined timeline.

The sample also stops on every warning returned by request validation. That is this example's acceptance policy. In a production system, investigate warnings and document any intentional exception rather than silently ignoring them.

If you already have a saved template

Use the stored-template preview endpoint to inspect a dataset against its current design:

POST /api/templates/{id}/preview
x-api-key: YOUR_API_KEY
Content-Type: application/json

{"variables":{"title":"October release status","items":[],"showCta":false,"cta":"Review the final render"}}

Replace {id} with your saved template ID. This shows the HTTP request shape; the runnable download uses inline validation instead.

A successful template preview returns HTTP 200 with project and stats. It is a JSON dry run: it does not queue a preview video, return a job ID, or generate a media URL. Check project.scenes, project.duration, and its resolved text. To obtain warnings and a credit estimate, send that resolved project to the validation endpoint under payload.

Operation Useful response fields Creates a video?
Validate an inline or stored-template request valid, payload, warnings, creditsRequired No
Preview a stored template project, stats No
Submit a render jobId, then a completed job with an output URL Yes, after completion

Keep a copy of the checked template and fixtures with your application release. An existing template ID points to its current saved design; an old successful check does not approve later edits.

Render one case and inspect the video

Once the matrix passes and you accept the current credit estimate, run:

npm run render

This command spends render credits. It reruns the matrix, checks the representative estimate against MAX_RENDER_CREDITS, submits only that case, and saves its job ID before tracking completion. It renders the resolved snapshot from validation, keeping the checked content and design together.

When the job completes, the script downloads out/release-status.mp4. It also saves out/request.json, out/run.json, and out/job.json so you can inspect what was submitted and which job produced the result.

This is the representative output: an introduction, two repeated status scenes, and a final call to action (CTA). Each scene lasts three seconds. Three half-second fades overlap the scene boundaries, making the finished video 10.5 seconds, not 12.

October release status → API: Ready → Editor: Ready → Review the final render. The example is silent and uses only text, so no source-media upload is needed.

Watch the whole video. Confirm the title, both statuses, and the CTA are correct. Check text wrapping, contrast, fades, reading time, and the final frame. A completed job proves that an output was produced, not that its design is approved.

The command renders one representative case, not the entire matrix. Before using your own template in production, visually review any state that changes the layout, especially long copy, different fonts or languages, an empty list, and a removed CTA. Add further renders where those changes cannot be approved from an editor preview. Each additional render consumes credits.

Resume an existing job after an interruption

npm run resume

Resume reads the saved ID, tracks the same job, and downloads its result. It does not submit another render. The script waits for state: "completed" and a usable output URL; progress: 100 alone is not completion. A failed job stops with its failedReason.

If the render command stops before saving an ID, check Renders at app.zvid.io. A missing response does not prove that the job was never submitted. After identifying that job, use:

npm run resume -- --job-id YOUR_JOB_ID

The example records a submission attempt before sending it, blocking accidental repeat submissions. To deliberately make a new video, first inspect the previous job and rename out to preserve its record. Then validate and render again. For completion states, output expiry, and failure handling, see the render lifecycle.

Turn the sample into your own acceptance checks

Change data/template.json and data/cases.json together. Start with the smallest useful input, a typical input, the largest allowed input, every important condition, and known invalid data. Keep independent cases for separate boundaries so a failure points to one clear cause.

Write expectations for the values and sequence your business needs, not just the JSON shape Zvid accepts. If an empty list should stop your application, reject it before validation; this sample deliberately allows an introduction-only video.

If you add images, footage, or audio, also inspect those sources and their availability, format, crop, and timing. Structural validation does not prove that every remote file can be downloaded or that it will remain available when a render runs.

Run the matrix after a change to data mapping, variable names, defaults, scene timing, or design. Then review the affected visuals before sending a production batch. If a check fails, the invalid-JSON troubleshooting guide helps you separate request errors from media and render-job failures.

Frequently asked questions

Does Zvid template preview create a video or use render credits?

No. POST /api/templates/{id}/preview resolves and validates a saved template, returning HTTP 200 with project and stats. It creates no render job or media and consumes no render credits. Submit a separate render to create a video.

Can I validate a Zvid template without saving it in my account?

Yes. Send the project under payload and replacement values under request-level variables to POST /api/render/validate/api-key. The supplied example uses this route, so it requires neither a template ID nor a spare saved-template slot.

Does successful validation prove that long text will fit in a video?

No. Validation can check project structure and report some layout warnings, but it does not approve the finished typography. Review long-copy states at the intended output size and render the affected states when necessary.

Why does a four-scene video last 10.5 seconds instead of 12 seconds?

The sample's three half-second fades overlap adjacent scenes. Four three-second scenes total 12 seconds, and the 1.5 seconds of overlap reduce the finished timeline to 10.5 seconds.

What should I do if my render stops at 100 percent progress?

Keep checking the saved job until its state is completed or failed. Progress is a hint, not the completion condition. In the supplied example, run npm run resume to continue tracking that job without creating another paid render.

Share