Edit a JSON tree and re-render
Mutate an editable image ad's JSON tree via PATCH and wait for the new render.
Editable image ads return a JSON tree alongside the PNG. You can mutate the tree and PATCH the ad — the API re-renders and returns a new version.
When to use this
- Building a custom ad editor.
- Tweaking copy or images after the AI's first pass.
- Producing many variants of one base ad with small differences.
For one-shot overrides, use node_overrides at creation time — simpler, no patch needed.
End-to-end flow
sequenceDiagram
participant App as Your app
participant API as Static Ads Lab API
App->>API: POST /v1/image-ads (editable: true)
API-->>App: 202 { id, status: processing }
Note right of API: pipeline runs
App->>API: GET /v1/image-ads/:id (poll/SSE)
API-->>App: completed { image_url, current_json_tree }
App->>App: mutate tree
App->>API: PATCH /v1/image-ads/:id { current_json_tree }
API-->>App: 200 { status: processing }
Note right of API: re-render
App->>API: GET /v1/image-ads/:id
API-->>App: completed { new image_url }Step by step
1. Generate the editable ad
const r = await fetch("https://api.staticadslab.com/v1/image-ads", {
method: "POST",
headers: {
"X-API-Key": process.env.SAL_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
design_template_id: "dt_...",
brand_id: "brand_...",
product_id: "prod_...",
audience_id: "aud_...",
editable: true,
}),
});
const { data: created } = await r.json();
const ad = await waitForCompletion(created.id);2. Locate the node you want to change
JSON trees vary by template. Walk the tree to find a text or image node by some heuristic — characters content for text, role/name for images.
function findTextNodeByCharacters(tree, query) {
const stack = [tree];
while (stack.length) {
const node = stack.pop();
if (node.type === "TEXT" && node.characters?.includes(query)) return node;
for (const child of node.children ?? []) stack.push(child);
}
return null;
}
const headline = findTextNodeByCharacters(ad.current_json_tree, "Limited time");
headline.characters = "Black Friday: 40% off";3. PATCH the full tree back
Always send the entire updated tree. The API does not accept diffs.
await fetch(`https://api.staticadslab.com/v1/image-ads/${ad.id}`, {
method: "PATCH",
headers: {
"X-API-Key": process.env.SAL_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ current_json_tree: ad.current_json_tree }),
});After PATCH, the resource transitions back to status: "processing" while the new render runs. Poll or SSE until completed.
4. Read the new render
const updated = await waitForCompletion(ad.id);
console.log(updated.image_url);The previous render is preserved as a version (iav_…) — see GET /v1/image-ads/:id/versions.
Image swaps
Image fills carry a imageFillUrl — a public URL. To swap an image:
- Upload the new image via
POST /v1/images(multipart). - Use the returned
data.srcas theimageFillUrl. - Update the tree node and
PATCH.
Don't embed base64 image data in the JSON.
Versioning
Every successful PATCH creates a new version. List them:
GET /v1/image-ads/:id/versionsGet a specific version:
GET /v1/image-ads/:id/versions/:versionIdVersions are immutable. Use them as an audit log of changes, or to roll back by re-applying an older version's tree via PATCH.
Pitfalls
- A
PATCHon a flat ad (editable: false) returns an error — flat ads have no tree. current_json_treeisnulluntil the original generation completes. Wait forstatus: "completed"before reading or patching.- Don't
PATCHwhile the resource isprocessing— wait for the previous job to complete first. - Sending a malformed tree (missing required fields, invalid
imageFillUrl) returns422 VALIDATION_ERROR.