Static Ads Lab
Concepts

Editable image ads

PNG plus a structured JSON tree you can mutate via PATCH and re-render. Versioned and Figma-syncable.

An editable image ad is an image ad generated with "editable": true. In addition to the Meta-ready PNG, you get a JSON tree — a structured document representing every visual element. You can PATCH the tree to change text, images, colors, or layout, and the API re-renders the PNG.

When to use editable ads

  • You're building a custom ad editor on top of the API.
  • You want programmatic ad variants (e.g., 50 ads with the same layout but different headlines).
  • You want to round-trip an ad through Figma — see Figma sync.

If you just need one Meta-ready PNG and don't intend to modify it, use a flat image ad — it's $1.00 vs $4.00.

Lifecycle

stateDiagram-v2
  [*] --> processing: POST editable=true (202)
  processing --> completed: image_url + current_json_tree ready
  processing --> failed
  completed --> patched: PATCH /v1/image-ads/:id
  patched --> completed: re-render done
  completed --> [*]

Every successful PATCH creates a new version (iav_…) you can list and replay.

Endpoints

All editable ads share the Image ads endpoints. The editable-only endpoints are:

MethodPathPurpose
PATCH/v1/image-ads/:idUpdate the JSON tree
GET/v1/image-ads/:id/versionsList versions
GET/v1/image-ads/:id/versions/:versionIdGet a specific version
POST/v1/image-ads/syncSync from Figma

Common patterns

Generate, then change a headline

const create = await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_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 create.json();
const ad = await waitForCompletion(created.id);

const headlineNodeId = findNodeIdByRole(ad.current_json_tree, "headline");
ad.current_json_tree.nodes[headlineNodeId].characters = "Black Friday: 40% off";

await fetch(`https://api.staticadslab.com/v1/image-ads/${ad.id}`, {
  method: "PATCH",
  headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ current_json_tree: ad.current_json_tree }),
});

A PATCH re-renders the PNG asynchronously; the resource returns to status: "processing" and back to completed when the new render is ready. Poll GET /v1/image-ads/:id until the new image_url is available.

Use node_overrides instead of PATCH

If you know upfront which nodes you want to override, use node_overrides at creation time. It's simpler and skips the patch round-trip:

await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    design_template_id: "dt_...",
    brand_id: "brand_...",
    product_id: "prod_...",
    audience_id: "aud_...",
    editable: true,
    node_overrides: {
      "node-id-headline": { characters: "Black Friday: 40% off" },
      "node-id-hero": {
        imageFillUrl: "https://cdn.example.com/black-friday-hero.jpg",
      },
    },
  }),
});

Override values are applied verbatim and never modified by the AI pipeline.

List versions

const response = await fetch(
  `https://api.staticadslab.com/v1/image-ads/${adId}/versions`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const { data } = await response.json();
data.forEach((v) => console.log(v.version_number, v.image_url));

Pitfalls

  • current_json_tree is always null for flat ads. If you need to modify, you must request "editable": true at creation time. There is no path to convert a flat ad into an editable one.
  • A PATCH returns the resource as processing while the new render runs. Don't read image_url until you see completed again.
  • Image fills must be public URLs in imageFillUrl. The API does not accept base64 image data in JSON.
  • Versions are immutable. Once written, a version's json_tree and image_url cannot be changed.

Prompt for your agent

Read https://www.staticadslab.com/docs/resources/editable-image-ads.mdx and write a function patchHeadline(adId, newHeadline) that updates an editable ad's headline node and waits for the re-render to complete.