@zvid/sdk
v0.2.0
Published
Official TypeScript/JavaScript SDK for the Zvid JSON-to-video/image rendering API
Maintainers
Readme
@zvid/sdk — official TypeScript SDK
Typed TypeScript/JavaScript client for the Zvid JSON-to-video/image rendering API. Fetch-based with zero runtime dependencies, ESM + CJS, strict types derived from the maintained API contract, automatic transient-failure retries, polling helpers, media uploads, and timing-safe webhook signature verification.
npm install @zvid/sdkRequires Node 18+ (native fetch). The API client itself is runtime-agnostic;
the webhook helpers use node:crypto.
Quickstart
import { ZvidClient, outputUrl } from "@zvid/sdk";
const zvid = new ZvidClient(); // reads ZVID_API_KEY; create one at https://app.zvid.io/api-keys
const job = await zvid.renders.createImage({
payload: {
type: "image",
width: 1200,
height: 630,
visuals: [{ type: "TEXT", text: "Hello Zvid", position: "center-center" }],
},
});
const done = await zvid.waitForRender(job.jobId, { timeoutMs: 120_000 });
console.log(outputUrl(done)); // https://cdn.zvid.io/...Configuration
| Option | Env var | Default |
| --- | --- | --- |
| apiKey | ZVID_API_KEY | — (required) |
| baseUrl | ZVID_BASE_URL | https://api.zvid.io |
| fetch | — | global fetch |
| maxRetries | — | 3 |
| retryBaseDelayMs | — | 1000 |
| retryMaxDelayMs | — | 30000 |
Network failures and HTTP 429, 502, 503, and 504 responses are retried with
exponential backoff and jitter. Retry-After is honored up to retryMaxDelayMs.
Set maxRetries: 0 when the caller must never repeat a request. onRetry can feed
application logs or metrics without replacing the retry implementation.
Surface
| Namespace | Methods |
| --- | --- |
| zvid.account | profile |
| zvid.apiKeys | list, create, update, stats, revoke / delete |
| zvid.authoring | getSchema, listElements, getElementDocs, getExamples, creativePlan, repair, validate (plan-aware; no render credits) |
| zvid.renders | create, createImage, createBulk, createImageBulk, listBulk, getBulk |
| zvid.jobs | get, list, wait |
| zvid.templates | list, get, create, update, duplicate, preview, archive / delete |
| zvid.projects | list, get, create, update, delete |
| zvid.uploads | list, create, delete |
| zvid.webhooks | list, get, create, update, delete, test, deliveries |
| zvid.credits | balance, transactions, usageStats |
Renders are asynchronous: every renders.create* call returns { jobId }.
Poll zvid.jobs.get(jobId) yourself, or block with
zvid.waitForRender(jobId, { timeoutMs, pollIntervalMs, signal }) — it resolves with
the terminal JobStatus (use the outputUrl() / thumbnailUrl() helpers on it),
rejects with RenderFailedError on failure and WaitTimeoutError on timeout, and
supports AbortSignal.
Every render call takes exactly one of payload (inline project JSON, typed as
RenderPayload) or template (stored tpl_… id), plus optional variables,
overrides, and a one-off webhookUrl. The authoritative payload schema is
published at docs.zvid.io (render-payload.schema.json).
zvid.authoring.validate() always resolves for schema validation: check its valid
field. Invalid payloads return { valid: false, errors, warnings }; authentication,
network, and other API failures still throw.
Uploads
Upload a browser File or a Blob created in Node.js. The returned CDN URL can be
used directly as an image, video, GIF, or audio element source.
import { readFile } from "node:fs/promises";
const bytes = await readFile("./poster.png");
const poster = await zvid.uploads.create(
new Blob([bytes], { type: "image/png" }),
{ fileName: "poster.png", width: 1200, height: 630 },
);
console.log(poster.url);Errors
For AI generation, read zvid.authoring.getSchema() and the relevant element docs, start from a validated example, then repair and validate before calling zvid.renders.create*.
All API errors extend ZvidAPIError (with .status, .error, .details, .body):
| Class | When |
| --- | --- |
| AuthenticationError | 401 |
| InsufficientCreditsError | 402 — has .creditsRequired / .creditsAvailable |
| NotFoundError | 404 |
| RateLimitError | 429 — has .retryAfter (seconds) |
| RenderFailedError | thrown by waitForRender when the job fails (.job) |
| WaitTimeoutError | thrown by waitForRender on timeout |
Webhooks
Deliveries to registered endpoints are signed:
X-Zvid-Signature: sha256=hex(HMAC_SHA256(secret, "<X-Zvid-Timestamp>.<raw body>")).
import { verifyWebhookSignature } from "@zvid/sdk";
// Express example — use the RAW body (express.raw / rawBody), not re-serialized JSON
app.post("/hooks/zvid", express.raw({ type: "application/json" }), (req, res) => {
if (!verifyWebhookSignature(req.body, req.headers, process.env.ZVID_WEBHOOK_SECRET!)) {
return res.status(400).end();
}
const event = JSON.parse(req.body.toString());
// event.event === "render.completed" | "render.failed", event.data.url, …
res.status(200).end();
});verifyWebhookSignature uses crypto.timingSafeEqual and rejects deliveries older
than 5 minutes ({ toleranceSeconds: null } disables the freshness check). It accepts
fetch Headers, Node request headers, or plain objects. Per-request webhookUrl
deliveries are not signed — only account endpoints are.
Development
npm install
npm run typecheck && npm test && npm run buildLive smoke test against a running orchestrator (spends ~1 credit):
ZVID_API_KEY=zvid_… ZVID_BASE_URL=http://localhost:4000 node examples/e2e.mjsPublishing (manual)
Not published yet. To release version 0.2.0:
npm run prepublishOnly # typecheck + tests + build
npm publish # publish the public `@zvid/sdk` package to npm