How to Generate Editable Multi-Layer Fabric.js Templates via REST API (Prisma V1.0 Tutorial)
Most image APIs give you a PNG. If your product needs designs that people keep editing, like retyping a headline, swapping a photo or translating a banner, a PNG is a dead end. This tutorial generates a complete design w
Most image APIs give you a PNG. If your product needs designs that people keep editing, like retyping a headline, swapping a photo or translating a banner, a PNG is a dead end. This tutorial generates a complete design with Prisma V1.0 and gets it back as an editable multi-layer template you can load into Fabric.js.
What comes back:
- canvas JSON in the
legacy-fabric-v1format, with live text objects, native shapes and image objects; - private raster layers (PNG) and a JPEG preview;
- your own photos, placed untouched as separate image objects (up to five).
Before you start
- A Prisma developer workspace you own: sign in on PicStudio's Prisma page.
- An API key with
generations:write,jobs:readandassets:read. It is shown once, so keep it on your server. - Prisma Credits. One-time refills start at $10 for five designs, and each generation costs 500 credits.
- Node.js 18 or later for the examples (built-in
fetch).
Step 1: Submit a generation
curl --fail-with-body 'https://picstudio.aismartcontent.co/api/v1/generations' \
-H "Authorization: Bearer $PRISMA_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: campaign-2026-001' \
-d '{"model":"prisma-v1.0","prompt":"An original matcha campaign with a large heading and separate product photography.","name":"Matcha launch","aspect_ratio":"4:5"}'
The API answers 202 Accepted with a data envelope. Save data.job_id. Fields worth knowing:
-
prompt: 20 to 1,600 characters. -
aspect_ratio:1:1,4:5,9:16,16:9or1.91:1(default1.91:1). -
material_images: up to five photos as{mime_type, data_base64}, each placed untouched (JPEG, PNG or WebP; 2 MiB each, 5 MiB in total). -
reference_imagewithreference_mode(moodorstyle) to borrow a feeling or a palette. - The
Idempotency-Keyheader (8 to 96 safe characters): send a retry with the same key and you are never charged twice.
Step 2: Poll the job
const BASE = 'https://picstudio.aismartcontent.co/api/v1';
const auth = { Authorization: `Bearer ${process.env.PRISMA_API_KEY}` };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function waitForJob(jobId) {
for (;;) {
const res = await fetch(`${BASE}/jobs/${jobId}`, { headers: auth });
if (res.status === 429) {
await sleep((Number(res.headers.get('Retry-After')) || 15) * 1000);
continue;
}
const { data } = await res.json();
if (data.status === 'completed') return data;
if (data.status === 'failed' || data.status === 'cancelled') {
throw new Error(data.error?.message ?? `Job ${data.status}`);
}
await sleep(5000); // the docs ask for one poll every 5 seconds
}
}
The states are queued, processing, completed, failed and cancelled, and data.progress runs from 0 to 100.
Step 3: Download the editable template
async function getTemplate(jobId) {
const res = await fetch(`${BASE}/jobs/${jobId}/result`, { headers: auth });
const body = await res.json();
const result = body.data ?? body;
return JSON.parse(result.template_data); // legacy-fabric-v1 canvas JSON
}
Image objects in that JSON point to /api/v1/jobs/{job_id}/assets/.... Every one of those downloads needs the bearer key, so fetch the files on your backend and serve your own copies. Never put the key in a URL.
Step 4: Load it into Fabric.js
const canvas = new fabric.Canvas('editor');
canvas.loadFromJSON(templateJson, () => canvas.renderAll());
Text stays text, so users can retype or translate it. Shapes stay vector, and each photo is its own object. The format is legacy-fabric-v1, so check the docs for the Fabric.js version it targets before you wire it into a newer editor.
Limits and good habits
- 60 requests and 5 new generations per minute per key, and 2 simultaneous generations per workspace.
- Honour
Retry-Afteron429, and don't regenerate automatically. - Errors come in an envelope with
error.codeanderror.request_id. Keep the request ID for support. - V1.0 has no SDK or webhooks. Review every result, because fonts may be substituted and fine details can need a touch-up.
Wrapping up
That is the whole loop: submit, poll, fetch, load. For pricing and the full feature list, read the Prisma AI design model overview, and find the reference on PicStudio's Prisma page.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.