Skip to content
JamFrame
Docs menu

Generate a template with AI

Describe a design in plain words and get a finished, saved template back from one API call, ready to render. Body fields, waiting, polling, costs and limits.

POST /v1/designs/generate asks the design agent to make a new template from a description. It lays out the pages, writes sample text, uses your brand colors, fonts and logo, and gives keys to the layers a render will change, such as title, photo or cta. The template is saved in your workspace, so you can render it right away. To do the same in the app, see Design with AI.

Send a request

curl https://api.jamframe.com/v1/designs/generate \
  -H "Authorization: Bearer $JAMFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Announce our summer sale, 30% off everything this weekend. Bold and playful.",
    "size": "instagram-portrait"
  }'
const res = await fetch("https://api.jamframe.com/v1/designs/generate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.JAMFRAME_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    prompt: "Announce our summer sale, 30% off everything this weekend. Bold and playful.",
    size: "instagram-portrait",
  }),
});
const { data } = await res.json();
// 201: data.design is ready. 202: poll data.jobId.
console.log(res.status, data);
import os
import requests

res = requests.post(
    "https://api.jamframe.com/v1/designs/generate",
    headers={"Authorization": f"Bearer {os.environ['JAMFRAME_API_KEY']}"},
    json={
        "prompt": "Announce our summer sale, 30% off everything this weekend. Bold and playful.",
        "size": "instagram-portrait",
    },
)
data = res.json()["data"]
# 201: data["design"] is ready. 202: poll data["jobId"].
print(res.status_code, data)

Body fields

Field Required What it does
prompt Yes What to design, in plain words. Up to 4,000 characters.
size No The canvas size: a preset id such as instagram-post, instagram-story or linkedin-carousel, a short name (square, portrait, story, landscape, widescreen, thumbnail, pin), or "WIDTHxHEIGHT" such as "1080x1350". Default: 1080 × 1350.
width, height No Instead of size: whole numbers from 16 to 10,000 pixels.
pages No How many pages, from 1 to 10. More than 1 makes a carousel. Default: 1.
brand No Which brand kit to use. See below. Default: your workspace’s brand library.
referenceImageUrl No The https link of one image for the agent to look at or use: PNG, JPEG, GIF or WebP, up to 10 MB.
name No The template’s name, up to 120 characters. Default: a name made from the prompt.
async No true answers 202 at once, without waiting. Default: false.

Any other field is a 400 VALIDATION_ERROR.

If the design uses your reference image, JamFrame saves a copy in your brand library.

The brand kit

brand value What the agent uses
Left out, or true The colors, fonts and logo in your workspace’s brand library.
false No brand kit.
An object The colors, fonts and logo you send. Your brand library’s other images can still be used.

The object takes three fields, each optional:

  • colors: up to 12 hex colors, such as "#0b1f4d". The first is the main color.
  • fonts: up to 4 font names. Each must be a font in your brand library or a Google font the editor offers.
  • logo: the link of an image in your brand library, or any https image link.
{
  "prompt": "Five tips for running better remote meetings",
  "size": "linkedin-carousel",
  "pages": 5,
  "brand": { "colors": ["#0b1f4d", "#ff6b5b"], "fonts": ["Poppins", "Inter"] }
}

The answer

A design usually takes 10 to 40 seconds. The request waits up to about 25 seconds:

  • 201 Created: the template is ready. data.design holds it.
  • 202 Accepted: the agent is still working. The answer is {"data": {"jobId": "…", "status": "running"}}, and the Location header has the link to poll. With "async": true you always get this answer.

A 201 answer looks like this (some fields left out):

{
  "data": {
    "jobId": "cmw9uoeb9000binw52g648888",
    "status": "succeeded",
    "message": "A bold coral and navy sale post with a photo slot, the headline, a subline and a \"Shop now\" button.",
    "changes": ["Page 1: background #fff7ed", "Added image \"Photo\"", "Added text \"Headline\""],
    "creditsCharged": 5,
    "createdAt": "2026-11-14T09:12:44.000Z",
    "finishedAt": "2026-11-14T09:13:02.000Z",
    "design": {
      "id": "clx8m2k3p0002abcd1234efgh",
      "name": "Announce our summer sale, 30% off everything",
      "orientation": "portrait",
      "pages": 1,
      "parameters": [
        { "key": "photo", "type": "image", "pages": [1], "example": "" },
        { "key": "title", "type": "text", "pages": [1], "example": "Summer sale" }
      ],
      "sizes": [],
      "editorUrl": "https://app.jamframe.com/designs/clx8m2k3p0002abcd1234efgh/edit"
    }
  }
}

design has the same fields as GET /v1/designs/{id}, plus editorUrl, a link that opens the template in the editor.

Poll a running job

Call GET /v1/designs/generate/{jobId} every few seconds until status is no longer running:

status Meaning
running The agent is still working.
succeeded Done. The answer has design, as in the 201 answer.
failed It failed. error holds the error code and message. No AI credits were charged.
async function waitForDesign(jobId) {
  for (;;) {
    const res = await fetch(`https://api.jamframe.com/v1/designs/generate/${jobId}`, {
      headers: { Authorization: `Bearer ${process.env.JAMFRAME_API_KEY}` },
    });
    const { data } = await res.json();
    if (data.status === "succeeded") return data.design;
    if (data.status === "failed") throw new Error(`${data.error.error}: ${data.error.message}`);
    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
}

A job that is still running 15 minutes after it started is marked failed with AI_TIMEOUT. A job id from another workspace answers 404 NOT_FOUND.

Use the new template

The new template shows on the Templates page like any other, and fires a design.created webhook event. Render it at once with its id as templateId. The keys in parameters are the values you can send in modifications:

{
  "templateId": "clx8m2k3p0002abcd1234efgh",
  "modifications": { "title": "Summer sale", "photo": "https://example.com/beach.jpg" }
}

See Render images and PDFs. Open editorUrl to change the design by hand.

Cost and limits

  • Each design costs 5 AI credits, from the same AI credits as the other AI features. A failed design is refunded. See Credits.
  • Each workspace can generate 30 designs per 10 minutes. More answer 429 RATE_LIMITED. This counts on top of your per-minute limit.

Errors

Errors that happen while the request waits come back as the answer. Errors after a 202 show in the job’s error.

Status error What to do
400 VALIDATION_ERROR A field is wrong or unknown, or the reference image couldn’t be used. Read message.
402 AI_CREDITS_EXCEEDED Your AI credits for this period are used up.
422 AI_CONTENT_REFUSED The AI provider refused the prompt under its content policy. Change the prompt.
429 RATE_LIMITED Over 30 designs in 10 minutes, or over your per-minute limit. Wait for Retry-After seconds.
502 AI_BAD_OUTPUT or AI_UPSTREAM_ERROR The AI didn’t make a usable design, or the provider failed. Try again, maybe with a clearer prompt.
503 AI_UNAVAILABLE AI is off right now. Try later.

No AI credits are charged when a design fails. See Errors for the full list.

Last updated

Still stuck?

Email [email protected] and a real person will reply. Tell us the template you were using and what you expected to happen.