Skip to content
jsontoimg
Start free

SDKs and reference

Node.js

@j2i/sdk is the official TypeScript client for the jsontoimg Render API. Use it in Node.js, Bun, or any TypeScript project to create renders, wait for results, download assets, and run batches.

For shell scripts and CI, see the CLI. For raw HTTP endpoints, see the API reference. Python and PHP call the same HTTP API until those SDKs ship.

Install

npm install @j2i/sdk
pnpm add @j2i/sdk
yarn add @j2i/sdk
bun add @j2i/sdk

Auth and config

Create an API key in the dashboard under Integrations → API keys, then pass it to the client. See Authentication.

import { createClient } from "@j2i/sdk";

const client = createClient({
  apiKey: process.env.JSONTOIMG_API_KEY!,
});
Option Environment variables (first match wins) Default
apiKey JSONTOIMG_API_KEY, RENDER_API_KEY Required
baseUrl JSONTOIMG_API_URL, JSONTOIMG_BASE_URL, RENDER_BASE_URL https://app.jsontoimg.com

Use the API origin only for baseUrl (no trailing slash). /api/v1 and /mcp suffixes are stripped if present. Requests are authenticated with Authorization: Bearer <apiKey>.

Quick start

import { createClient } from "@j2i/sdk";

const client = createClient({
  apiKey: process.env.JSONTOIMG_API_KEY!,
});

const job = await client.renderAndWait({
  template: "<design-id>",
  format: "png",
  layers: { headline: { text: "Hello" } },
});

if (job.status === "done") {
  console.log(job.id, job.assetUrl);
}

renderAndWait submits a render, waits up to 60 seconds by default, and can write the finished file to disk with out.

Client methods

createRender

createRender(
  body: CreateRenderRequest,
  opts?: { wait?: number }
): Promise

Creates a render. Pass wait (seconds) to block until the job finishes or the wait window ends. Without wait, you get a queued job and can poll or call waitForRender later.

const accepted = await client.createRender({
  template: "<design-id>",
  format: "png",
});

const done = await client.createRender(
  { template: "<design-id>", format: "png" },
  { wait: 60 }
);

getRender

getRender(id: string): Promise

Fetches the current status of a render job.

const job = await client.getRender(renderId);
console.log(job.status);

waitForRender

waitForRender(
  id: string,
  opts?: { timeout?: number }
): Promise

Waits for a render to finish. If the job is still running, the response may include retryAfterMs. See SSE and wait.

const result = await client.waitForRender(renderId, { timeout: 60 });

downloadRenderAsset

downloadRenderAsset(id: string): Promise<{
  buffer: Buffer;
  contentType: string;
}>

Downloads the finished asset. The job must be done. Uses your API key. For <img> / OG tags, use job.assetUrl (includes ?sig=) or signImageUrl.

import { writeFile } from "node:fs/promises";

const { buffer, contentType } = await client.downloadRenderAsset(renderId);
await writeFile("out.png", buffer);
console.log(contentType);

signImageUrl

signImageUrl(body: SignImageRequest): Promise

Mints an HMAC-signed URL for CMS / Open Graph. Crawlers fetch it with no API key. See URL rendering.

const { url } = await client.signImageUrl({
  template: "<design-id>",
  format: "png",
  layers: { headline: { text: "Hello" } },
});

signImageUrls

signImageUrls(body: SignImageBatchRequest): Promise

Mints up to 50 signed URLs in one call. Same contract as signImageUrl, not queued batch renders.

const { urls } = await client.signImageUrls({
  jobs: [
    { template: "<design-id>", layers: { headline: { text: "Hello" } } },
    { template: "<design-id>", layers: { headline: { text: "World" } } },
  ],
});

renderAndWait

renderAndWait(
  body: CreateRenderRequest,
  opts?: { wait?: number; out?: string }
): Promise

Creates a render with wait (default 60 seconds). If you pass out and the job completes, the asset is saved to that path.

const job = await client.renderAndWait(
  {
    template: "<design-id>",
    format: "png",
    layers: { headline: { text: "Hello" } },
  },
  { wait: 90, out: "./hello.png" }
);

listTemplates

listTemplates(opts?: {
  project?: string;
  limit?: number;
  offset?: number;
}): Promise

Returns one page of templates (default 50, max 100). If hasMore is true, pass offset for the next page. Use name and description to pick one, then call getTemplateSchema.

const { templates, hasMore } = await client.listTemplates();
const sale = templates.find((item) =>
  item.description?.includes("sale")
);

getTemplateSchema

getTemplateSchema(
  id: string,
  opts?: { version?: number }
): Promise

Returns the named layers you can override for a template.

const schema = await client.getTemplateSchema("<template-id>", { version: 1 });
for (const layer of schema.layers) {
  console.log(layer.layerName, layer.layerType);
}

createBatch / getBatch

createBatch(body: CreateBatchRequest): Promise
getBatch(id: string): Promise

Queue up to 50 renders in one request, then check progress. See Batch renders.

const batch = await client.createBatch({
  jobs: [
    {
      template: "<design-id>",
      format: "png",
      layers: { headline: { text: "A" } },
    },
    {
      template: "<design-id>",
      format: "jpg",
      layers: { headline: { text: "B" } },
    },
  ],
});

const status = await client.getBatch(batch.id);
console.log(status.status, status.totals);

Request body

{
  "template": "<design-id>",
  "format": "png",
  "scale": 1,
  "transparent": false,
  "layers": {
    "headline": { "text": "Hello" }
  },
  "variables": { "plan": "pro" },
  "webhookUrl": "https://example.com/hook"
}
Field Notes
template Design ID (required)
version Optional design version
format png | jpg | webp | pdf | avif
scale Default 1
transparent Default false; png / webp / avif only
layers Layer name → property overrides
variables JSON for logic-rule conditions (source: variables)
webhookUrl Optional webhook when the job finishes
expandVariants Batch-only. { "layer": "<variant-set name>" } fans one job into one per variant

Errors

API failures throw JsontoimgApiError:

import { createClient, JsontoimgApiError } from "@j2i/sdk";

try {
  await client.createRender({ template: "<design-id>" });
} catch (err) {
  if (err instanceof JsontoimgApiError) {
    console.error(err.status, err.message, err.code, err.details);
  }
  throw err;
}
Property Meaning
status HTTP status
code API error code when present
details Extra validation or context
body Raw response body

See Errors for the full error shape.

Advanced

client.api is a typed low-level HTTP client if you need an endpoint shape the helpers do not wrap. The one helper-less path is GET /api/v1/renders/{id}/events (SSE).

const { data, error, response } = await client.api.GET(
  "/api/v1/renders/{id}/events",
  { params: { path: { id: renderId } } }
);

Useful exports include CreateRenderRequest, RenderJob, BatchStatus, TemplateList, TemplateSchema, JsontoimgApiError, resolveApiKey, and resolveBaseUrl.

  • CLI — same API from the shell
  • MCP — drive renders from Cursor, Claude, and VS Code
  • Webhooks — completion callbacks
  • URL rendering — signed URLs for CMS / OG
  • API reference — OpenAPI and Try it