List templates and renders
Find your templates and the keys each one takes, and look up past renders with their files, status and credits. The four read-only list and get calls.
Four calls let your code see what is in the workspace of your API key: its templates, the keys each template takes, and the renders it made. They are free and cost no credits. In the API, templates are called designs.
| Call | What it gives you |
|---|---|
GET /v1/designs |
Your templates, or the ready-made templates of the template library |
GET /v1/designs/{id} |
One template, with its keys, pages and saved sizes |
GET /v1/renders |
Your renders, newest first, with your credits for this period |
GET /v1/renders/{id} |
One render |
List templates
curl "https://api.jamframe.com/v1/designs?q=coffee&limit=10" \
-H "Authorization: Bearer $JAMFRAME_API_KEY"const res = await fetch("https://api.jamframe.com/v1/designs?q=coffee&limit=10", {
headers: { Authorization: `Bearer ${process.env.JAMFRAME_API_KEY}` },
});
const { designs, pagination } = await res.json();import os
import requests
res = requests.get(
"https://api.jamframe.com/v1/designs",
headers={"Authorization": f"Bearer {os.environ['JAMFRAME_API_KEY']}"},
params={"q": "coffee", "limit": 10},
)
designs = res.json()["designs"]| Query | What it does |
|---|---|
source |
workspace (default): your own templates. library: the template library, which any workspace can render by id. |
q |
Only templates whose name contains this text, ignoring case. Up to 100 characters. |
limit |
How many to return, from 1 to 100. Default: 25. |
cursor |
The nextCursor of the page before, to get the next page. |
Templates come newest first:
{
"designs": [
{
"id": "clx8m2k3p0002abcd1234efgh",
"name": "Coffee tips carousel",
"orientation": "square",
"pages": 3,
"createdAt": "2026-11-01T09:12:44.000Z",
"updatedAt": "2026-11-05T16:03:10.000Z"
}
],
"pagination": { "nextCursor": null, "hasMore": false }
}
orientation is square, portrait or landscape, from the size of the first page. pages is the number
of pages.
Get every page
When hasMore is true, call again with cursor set to nextCursor:
async function allDesigns() {
const all = [];
let cursor = null;
do {
const url = new URL("https://api.jamframe.com/v1/designs");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.JAMFRAME_API_KEY}` },
});
const { designs, pagination } = await res.json();
all.push(...designs);
cursor = pagination.hasMore ? pagination.nextCursor : null;
} while (cursor);
return all;
}
GET /v1/renders pages the same way.
Get one template and its keys
GET /v1/designs/{id} works for your own templates and for templates of the template library. It lists every
key a render can set. The answer looks like this (some fields left out):
{
"design": {
"id": "clx8m2k3p0002abcd1234efgh",
"name": "Coffee tips carousel",
"orientation": "square",
"pages": 3,
"createdAt": "2026-11-01T09:12:44.000Z",
"updatedAt": "2026-11-05T16:03:10.000Z",
"parameters": [
{ "key": "title", "type": "text", "pages": [1, 2, 3], "example": "5 ways to brew better coffee" },
{ "key": "photo", "type": "image", "pages": [1], "example": "https://example.com/images/beans.jpg" },
{ "key": "brand", "type": "variable", "pages": [1, 3], "example": "Acme Coffee" },
{
"key": "tagline",
"type": "text",
"pages": [3],
"example": "Fresh coffee, every morning",
"prompt": "Write a 6-word tagline about {{brand}}"
}
],
"sizes": [
{ "id": "story", "name": "Story / Reel", "width": 1080, "height": 1920 }
]
}
}
Each item of parameters is one key you can send in modifications:
| Field | What it is |
|---|---|
key |
The name to send, such as title. |
type |
What the key changes. See the table below. |
pages |
The pages the key appears on, numbered from 1. |
example |
The value saved in the template now. |
prompt |
Only for AI layers: the layer’s prompt. example is then its saved fallback. See AI text, images and translation. |
type |
What it changes | Send |
|---|---|---|
text |
A text layer | New text |
image |
An image layer | An image link |
qr |
A QR code | The text or link to encode |
shape |
A shape | A fill color |
variable |
A variable inside text, such as {{name}} |
The text to put in |
sizes lists the sizes saved in the template’s Resize tab. Send an id as response.size to render that
size. See Smart Resize.
A template that doesn’t exist, or belongs to another workspace, answers 404 NOT_FOUND.
List renders
GET /v1/renders lists every render of the workspace, newest first: renders from the API, test renders in the
app, dynamic URLs, signed URLs,
forms and workflows. Failed renders are listed too.
| Query | What it does |
|---|---|
status |
succeeded or failed. Any other value is a 400 VALIDATION_ERROR. |
designId |
Only renders of this template. |
limit |
How many to return, from 1 to 100. Default: 25. |
cursor |
The nextCursor of the page before. |
curl "https://api.jamframe.com/v1/renders?status=succeeded&limit=5" \
-H "Authorization: Bearer $JAMFRAME_API_KEY"
{
"renders": [
{
"id": "clx8n0q1r0005abcd1234efgh",
"createdAt": "2026-11-08T14:21:05.000Z",
"designId": "clx8m2k3p0002abcd1234efgh",
"designName": "Coffee tips carousel",
"source": "api",
"format": "jpeg",
"pages": 2,
"credits": 2,
"status": "succeeded",
"durationMs": 4210,
"outputs": [
{ "url": "https://cdn.jamframe.com/renders/…-p1.jpg?Expires=…", "page": 1, "expiresAt": "2026-11-09T09:05:00.000Z" },
{ "url": "https://cdn.jamframe.com/renders/…-p2.jpg?Expires=…", "page": 2, "expiresAt": "2026-11-09T09:05:00.000Z" }
],
"error": null,
"cached": false
}
],
"pagination": { "nextCursor": "clx8n0q1r0005abcd1234efgh", "hasMore": true },
"totals": {
"periodStart": "2026-11-01T00:00:00.000Z",
"periodEnd": "2026-12-01T00:00:00.000Z",
"creditsUsed": 342,
"creditsQuota": 1000,
"creditsRemaining": 658,
"workspaceCreditsUsed": 120
}
}
| Field | What it is |
|---|---|
designId, designName |
The template. Both are null if the template was deleted later. |
source |
Where the render came from: api, editor (a test render in the app), dynamic-url, signed-url, form or workflow. |
credits |
Render credits used. Failed renders cost nothing. |
outputs |
The files, one per page, as links that work for 5 minutes. Empty for failed renders and for renders with response.type: "base64". |
error |
Why a failed render failed, or null. |
cached |
true when a dynamic or signed URL was served from the cache, at no cost. |
totals shows your organization’s render credits for this billing period: creditsUsed, creditsQuota and
creditsRemaining (both null when there is no limit), and workspaceCreditsUsed, this workspace’s share. So
you can show how many credits are left without another call.
Get one render
GET /v1/renders/{id} returns one render as {"render": {…}}, with the same fields as a list item. Use it to
look up the renderId from a render answer, a render job, or a render.completed
webhook. A render of another workspace answers 404 NOT_FOUND.
Links work for 5 minutes
The links in outputs are new each time you call, and each works for 5 minutes (expiresAt). Download
the file soon, or call again for a fresh link. Files are deleted after 7 to 31 days, depending on where the
render came from (8 days for API renders), so links to older renders no longer work. See
How long files last. For a link that never expires, use a
dynamic URL.
Related
- Render a template: Render images and PDFs
- What you can send for each key: Modifications
- Your render log in the app: Renders
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.