Iteration Layer
Menu

How We Built Our Pitch Deck with Our Own API

The Problem with Slide Decks

We needed marketing slides. The kind you send to potential partners, drop into a pitch deck, or post on social media. Ten slides covering the product, features, integrations, use cases, and a CTA.

The obvious path: open Figma, drag boxes around, export PNGs. Update copy? Back to Figma. New product launched? Back to Figma. Font change? Back to Figma for every slide.

We already had an API that composites images from JSON layers. We already had layout layers that arrange children with gap, alignment, padding, and border radius. The slides were just images with text and colored boxes.

So we built them with our own API.

What We Built

Ten slides, generated from code, pixel-identical every time:

Hero slide

Problem slide

Product slide for Document Extraction

Integrations slide

Recipes slide

CTA slide

Each slide is a single API call. The canvas is 2540x1520 (2x resolution for crisp output on retina screens). The whole deck generates in under 2 seconds.

Layout Layers Are the Key

Most of the slides use layout layers — a layer type that arranges children horizontally or vertically, like CSS flexbox. You set direction, gap, alignment, padding, border radius, and background color. Children auto-size or use fixed dimensions.

This is what makes the slides possible without manual pixel positioning. A trust badge pill is a horizontal layout with an icon and auto-width text:

curl -X POST https://api.iterationlayer.com/image-generation/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": { "width_in_px": 2540, "height_in_px": 1520 },
    "layers": [
      {
        "index": 0,
        "type": "solid-color",
        "hex_color": "#0ea5e9"
      },
      {
        "index": 1,
        "type": "layout",
        "direction": "horizontal",
        "gap": 8,
        "vertical_alignment": "center",
        "background_color": "#38bdf8",
        "padding_top": 10,
        "padding_bottom": 10,
        "padding_left": 18,
        "padding_right": 22,
        "border_radius": 20,
        "position": { "x_in_px": 112.0, "y_in_px": 430.0 },
        "layers": [
          {
            "type": "image",
            "file": {
              "type": "base64",
              "name": "shield.svg",
              "base64": "<shield-icon-svg-base64>"
            },
            "dimensions": { "width_in_px": 18, "height_in_px": 18 }
          },
          {
            "type": "text",
            "text": "Zero data retention",
            "font_name": "PlusJakartaSans",
            "font_size_in_px": 15,
            "font_weight": "bold",
            "text_color": "#FFFFFF",
            "dimensions": { "width_in_px": "auto", "height_in_px": 22 }
          }
        ]
      }
    ]
  }'
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const trustPill = (iconBase64: string, label: string) => ({
  type: "layout" as const,
  direction: "horizontal" as const,
  gap: 8,
  vertical_alignment: "center" as const,
  background_color: "#38bdf8",
  padding_top: 10,
  padding_bottom: 10,
  padding_left: 18,
  padding_right: 22,
  border_radius: 20,
  layers: [
    {
      type: "image" as const,
      file: { type: "base64", name: "icon.svg", base64: iconBase64 },
      dimensions: { width_in_px: 18, height_in_px: 18 },
    },
    {
      type: "text" as const,
      text: label,
      font_name: "PlusJakartaSans",
      font_size_in_px: 15,
      font_weight: "bold",
      text_color: "#FFFFFF",
      dimensions: { width_in_px: "auto", height_in_px: 22 },
    },
  ],
});

const result = await client.generateImage({
  dimensions: { width_in_px: 2540, height_in_px: 1520 },
  layers: [
    { index: 0, type: "solid-color", hex_color: "#0ea5e9" },
    {
      index: 1,
      type: "layout",
      direction: "horizontal",
      gap: 24,
      position: { x_in_px: 112, y_in_px: 430 },
      layers: [
        trustPill(shieldIconBase64, "Zero data retention"),
        trustPill(flagIconBase64, "Made & hosted in the EU"),
        trustPill(cardIconBase64, "7-day trial"),
      ],
    },
  ],
});
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")


def trust_pill(icon_base64, label):
    return {
        "type": "layout",
        "direction": "horizontal",
        "gap": 8,
        "vertical_alignment": "center",
        "background_color": "#38bdf8",
        "padding_top": 10,
        "padding_bottom": 10,
        "padding_left": 18,
        "padding_right": 22,
        "border_radius": 20,
        "layers": [
            {
                "type": "image",
                "file": {"type": "base64", "name": "icon.svg", "base64": icon_base64},
                "dimensions": {"width_in_px": 18, "height_in_px": 18},
            },
            {
                "type": "text",
                "text": label,
                "font_name": "PlusJakartaSans",
                "font_size_in_px": 15,
                "font_weight": "bold",
                "text_color": "#FFFFFF",
                "dimensions": {"width_in_px": "auto", "height_in_px": 22},
            },
        ],
    }


result = client.generate_image(
    dimensions={"width_in_px": 2540, "height_in_px": 1520},
    layers=[
        {"index": 0, "type": "solid-color", "hex_color": "#0ea5e9"},
        {
            "index": 1,
            "type": "layout",
            "direction": "horizontal",
            "gap": 24,
            "position": {"x_in_px": 112, "y_in_px": 430},
            "layers": [
                trust_pill(shield_icon_base64, "Zero data retention"),
                trust_pill(flag_icon_base64, "Made & hosted in the EU"),
                trust_pill(card_icon_base64, "7-day trial"),
            ],
        },
    ],
)
package main

import il "github.com/iterationlayer/sdk-go"

func trustPill(iconBase64, label string) il.LayoutLayer {
    padding10 := 10
    padding18 := 18
    padding22 := 22
    radius20 := 20
    gap8 := 8
    return il.LayoutLayer{
        Type:               "layout",
        Direction:          "horizontal",
        Gap:                &gap8,
        VerticalAlignment:  "center",
        BackgroundColor:    "#38bdf8",
        PaddingTop:         &padding10,
        PaddingBottom:      &padding10,
        PaddingLeft:        &padding18,
        PaddingRight:       &padding22,
        BorderRadius:       &radius20,
        Layers: []il.Layer{
            il.ImageLayer{
                Type: "image",
                File: il.FileInput{Type: "base64", Name: "icon.svg", Base64: iconBase64},
                Dimensions: &il.Dimensions{WidthInPx: 18, HeightInPx: 18},
            },
            il.TextLayer{
                Type: "text", Text: label, FontName: "PlusJakartaSans",
                FontSizeInPx: 15, FontWeight: "bold", TextColor: "#FFFFFF",
                Dimensions: &il.Dimensions{HeightInPx: 22},
            },
        },
    }
}

func main() {
    client := il.NewClient("YOUR_API_KEY")

    gap24 := 24
    result, _ := client.GenerateImage(il.GenerateImageRequest{
        Dimensions: il.Dimensions{WidthInPx: 2540, HeightInPx: 1520},
        Layers: []il.Layer{
            il.NewSolidColorBackgroundLayer(0, "#0ea5e9"),
            il.LayoutLayer{
                Type: "layout", Index: 1, Direction: "horizontal",
                Gap: &gap24, Position: &il.Position{XInPx: 112, YInPx: 430},
                Layers: []il.Layer{
                    trustPill(shieldIconBase64, "Zero data retention"),
                    trustPill(flagIconBase64, "Made & hosted in the EU"),
                    trustPill(cardIconBase64, "7-day trial"),
                },
            },
        },
    })
    _ = result
}

The "auto" width on the text layer is what makes this work. The text renders at its natural width, then the layout wraps it with padding and a background. No measuring, no calculating pixel offsets.

Feature Cards with Background Layers

The product slides show feature cards in a 2x3 grid. Each card is a vertical layout with an icon, title, and description — positioned on the canvas with fixed dimensions:

curl -X POST https://api.iterationlayer.com/image-generation/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": { "width_in_px": 2540, "height_in_px": 1520 },
    "layers": [
      {
        "index": 0,
        "type": "solid-color",
        "hex_color": "#0ea5e9"
      },
      {
        "index": 1,
        "type": "layout",
        "direction": "vertical",
        "gap": 32,
        "background_color": "#0284c7",
        "padding": 56,
        "border_radius": 32,
        "dimensions": { "width_in_px": 780, "height_in_px": 480 },
        "position": { "x_in_px": 112.0, "y_in_px": 480.0 },
        "layers": [
          {
            "type": "image",
            "file": { "type": "base64", "name": "icon.svg", "base64": "<icon-base64>" },
            "dimensions": { "width_in_px": 56, "height_in_px": 56 }
          },
          {
            "type": "text",
            "text": "Schema-Driven Extraction",
            "font_name": "PlusJakartaSans",
            "font_size_in_px": 44,
            "font_weight": "bold",
            "text_color": "#FFFFFF",
            "dimensions": { "width_in_px": 668, "height_in_px": 68 }
          },
          {
            "type": "text",
            "text": "Define 17 typed fields and get structured JSON back.",
            "font_name": "PlusJakartaSans",
            "font_size_in_px": 36,
            "text_color": "#e0f2fe",
            "dimensions": { "width_in_px": 668, "height_in_px": 140 }
          }
        ]
      }
    ]
  }'
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const featureCard = (iconBase64: string, title: string, description: string) => ({
  type: "layout" as const,
  direction: "vertical" as const,
  gap: 32,
  background_color: "#0284c7",
  padding: 56,
  border_radius: 32,
  layers: [
    {
      type: "image" as const,
      file: { type: "base64", name: "icon.svg", base64: iconBase64 },
      dimensions: { width_in_px: 56, height_in_px: 56 },
    },
    {
      type: "text" as const,
      text: title,
      font_name: "PlusJakartaSans",
      font_size_in_px: 44,
      font_weight: "bold",
      text_color: "#FFFFFF",
      dimensions: { width_in_px: 668, height_in_px: 68 },
    },
    {
      type: "text" as const,
      text: description,
      font_name: "PlusJakartaSans",
      font_size_in_px: 36,
      text_color: "#e0f2fe",
      dimensions: { width_in_px: 668, height_in_px: 140 },
    },
  ],
});

const result = await client.generateImage({
  dimensions: { width_in_px: 2540, height_in_px: 1520 },
  layers: [
    { index: 0, type: "solid-color", hex_color: "#0ea5e9" },
    {
      index: 1,
      ...featureCard(iconBase64, "Schema-Driven Extraction", "Define 17 typed fields and get structured JSON back."),
      dimensions: { width_in_px: 780, height_in_px: 480 },
      position: { x_in_px: 112, y_in_px: 480 },
    },
  ],
});
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")


def feature_card(icon_base64, title, description):
    return {
        "type": "layout",
        "direction": "vertical",
        "gap": 32,
        "background_color": "#0284c7",
        "padding": 56,
        "border_radius": 32,
        "layers": [
            {
                "type": "image",
                "file": {"type": "base64", "name": "icon.svg", "base64": icon_base64},
                "dimensions": {"width_in_px": 56, "height_in_px": 56},
            },
            {
                "type": "text",
                "text": title,
                "font_name": "PlusJakartaSans",
                "font_size_in_px": 44,
                "font_weight": "bold",
                "text_color": "#FFFFFF",
                "dimensions": {"width_in_px": 668, "height_in_px": 68},
            },
            {
                "type": "text",
                "text": description,
                "font_name": "PlusJakartaSans",
                "font_size_in_px": 36,
                "text_color": "#e0f2fe",
                "dimensions": {"width_in_px": 668, "height_in_px": 140},
            },
        ],
    }


result = client.generate_image(
    dimensions={"width_in_px": 2540, "height_in_px": 1520},
    layers=[
        {"index": 0, "type": "solid-color", "hex_color": "#0ea5e9"},
        {
            "index": 1,
            **feature_card(icon_base64, "Schema-Driven Extraction", "Define 17 typed fields and get structured JSON back."),
            "dimensions": {"width_in_px": 780, "height_in_px": 480},
            "position": {"x_in_px": 112, "y_in_px": 480},
        },
    ],
)
package main

import il "github.com/iterationlayer/sdk-go"

func featureCard(iconBase64, title, description string) il.LayoutLayer {
    gap := 32
    padding := 56
    radius := 32
    return il.LayoutLayer{
        Type:            "layout",
        Direction:       "vertical",
        Gap:             &gap,
        BackgroundColor: "#0284c7",
        Padding:         &padding,
        BorderRadius:    &radius,
        Layers: []il.Layer{
            il.ImageLayer{
                Type: "image",
                File: il.FileInput{Type: "base64", Name: "icon.svg", Base64: iconBase64},
                Dimensions: &il.Dimensions{WidthInPx: 56, HeightInPx: 56},
            },
            il.TextLayer{
                Type: "text", Text: title, FontName: "PlusJakartaSans",
                FontSizeInPx: 44, FontWeight: "bold", TextColor: "#FFFFFF",
                Dimensions: &il.Dimensions{WidthInPx: 668, HeightInPx: 68},
            },
            il.TextLayer{
                Type: "text", Text: description, FontName: "PlusJakartaSans",
                FontSizeInPx: 36, TextColor: "#e0f2fe",
                Dimensions: &il.Dimensions{WidthInPx: 668, HeightInPx: 140},
            },
        },
    }
}

func main() {
    client := il.NewClient("YOUR_API_KEY")

    card := featureCard(iconBase64, "Schema-Driven Extraction", "Define 17 typed fields and get structured JSON back.")
    card.Index = 1
    card.Dimensions = &il.Dimensions{WidthInPx: 780, HeightInPx: 480}
    card.Position = &il.Position{XInPx: 112, YInPx: 480}

    result, _ := client.GenerateImage(il.GenerateImageRequest{
        Dimensions: il.Dimensions{WidthInPx: 2540, HeightInPx: 1520},
        Layers: []il.Layer{
            il.NewSolidColorBackgroundLayer(0, "#0ea5e9"),
            card,
        },
    })
    _ = result
}

When a card has dimensions set, the layout clips to that size and the border_radius is applied to the fixed container — not the auto-sized content. This means cards in a grid all have identical dimensions regardless of content length.

Centering with Fixed Dimensions

The CTA slide centers the trust badges horizontally. Layout layers don’t center children by default — they stack from the start. But when you set dimensions to the full slide width and horizontal_alignment to "center", the layout positions the auto-sized content in the middle:

curl -X POST https://api.iterationlayer.com/image-generation/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": { "width_in_px": 2540, "height_in_px": 1520 },
    "layers": [
      {
        "index": 0,
        "type": "solid-color",
        "hex_color": "#0ea5e9"
      },
      {
        "index": 1,
        "type": "layout",
        "direction": "horizontal",
        "horizontal_alignment": "center",
        "gap": 24,
        "dimensions": { "width_in_px": 2540, "height_in_px": 100 },
        "position": { "x_in_px": 0.0, "y_in_px": 860.0 },
        "layers": [
          {
            "type": "layout",
            "direction": "horizontal",
            "gap": 8,
            "vertical_alignment": "center",
            "background_color": "#38bdf8",
            "padding": 10,
            "border_radius": 20,
            "layers": [
              {
                "type": "text",
                "text": "Zero data retention",
                "font_name": "PlusJakartaSans",
                "font_size_in_px": 15,
                "font_weight": "bold",
                "text_color": "#FFFFFF",
                "dimensions": { "width_in_px": "auto", "height_in_px": 22 }
              }
            ]
          }
        ]
      }
    ]
  }'
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const result = await client.generateImage({
  dimensions: { width_in_px: 2540, height_in_px: 1520 },
  layers: [
    { index: 0, type: "solid-color", hex_color: "#0ea5e9" },
    {
      index: 1,
      type: "layout",
      direction: "horizontal",
      horizontal_alignment: "center",
      gap: 24,
      dimensions: { width_in_px: 2540, height_in_px: 100 },
      position: { x_in_px: 0, y_in_px: 860 },
      layers: [
        trustPill(shieldBase64, "Zero data retention"),
        trustPill(flagBase64, "Made & hosted in the EU"),
        trustPill(cardBase64, "7-day trial"),
      ],
    },
  ],
});
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

result = client.generate_image(
    dimensions={"width_in_px": 2540, "height_in_px": 1520},
    layers=[
        {"index": 0, "type": "solid-color", "hex_color": "#0ea5e9"},
        {
            "index": 1,
            "type": "layout",
            "direction": "horizontal",
            "horizontal_alignment": "center",
            "gap": 24,
            "dimensions": {"width_in_px": 2540, "height_in_px": 100},
            "position": {"x_in_px": 0, "y_in_px": 860},
            "layers": [
                trust_pill(shield_base64, "Zero data retention"),
                trust_pill(flag_base64, "Made & hosted in the EU"),
                trust_pill(card_base64, "7-day trial"),
            ],
        },
    ],
)
package main

import il "github.com/iterationlayer/sdk-go"

func main() {
    client := il.NewClient("YOUR_API_KEY")

    gap24 := 24
    result, _ := client.GenerateImage(il.GenerateImageRequest{
        Dimensions: il.Dimensions{WidthInPx: 2540, HeightInPx: 1520},
        Layers: []il.Layer{
            il.NewSolidColorBackgroundLayer(0, "#0ea5e9"),
            il.LayoutLayer{
                Type: "layout", Index: 1, Direction: "horizontal",
                HorizontalAlignment: "center", Gap: &gap24,
                Dimensions: &il.Dimensions{WidthInPx: 2540, HeightInPx: 100},
                Position:   &il.Position{XInPx: 0, YInPx: 860},
                Layers: []il.Layer{
                    trustPill(shieldBase64, "Zero data retention"),
                    trustPill(flagBase64, "Made & hosted in the EU"),
                    trustPill(cardBase64, "7-day trial"),
                },
            },
        },
    })
    _ = result
}

CTA slide with centered badges

This is the same pattern you’d use for centering a button, a logo, or any group of elements on a slide.

The Generative Wave Background

Every slide shares the same wave background — a deterministic SVG pattern seeded by a fixed string. The algorithm stacks wave bands with varying amplitude and color, rendered as Catmull-Rom spline paths. We covered the wave generator in How We Generate OG Images with Our Own API.

The wave SVG is rendered as a low-opacity image layer on top of a solid sky-blue background. These two layers appear at the bottom of every slide:

curl -X POST https://api.iterationlayer.com/image-generation/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": { "width_in_px": 2540, "height_in_px": 1520 },
    "layers": [
      {
        "index": 0,
        "type": "solid-color",
        "hex_color": "#0ea5e9"
      },
      {
        "index": 1,
        "type": "image",
        "file": { "type": "base64", "name": "waves.svg", "base64": "<wave-svg-base64>" },
        "position": { "x_in_px": 0.0, "y_in_px": 0.0 },
        "dimensions": { "width_in_px": 2540, "height_in_px": 1520 },
        "opacity": 10
      }
    ]
  }'
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const waveSvgBase64 = btoa(generateWaveSvg("marketing-slides", 2540, 1520));

const result = await client.generateImage({
  dimensions: { width_in_px: 2540, height_in_px: 1520 },
  layers: [
    { index: 0, type: "solid-color", hex_color: "#0ea5e9" },
    {
      index: 1,
      type: "image",
      file: { type: "base64", name: "waves.svg", base64: waveSvgBase64 },
      position: { x_in_px: 0, y_in_px: 0 },
      dimensions: { width_in_px: 2540, height_in_px: 1520 },
      opacity: 10,
    },
  ],
});
import base64
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

wave_svg_base64 = base64.b64encode(generate_wave_svg("marketing-slides", 2540, 1520)).decode()

result = client.generate_image(
    dimensions={"width_in_px": 2540, "height_in_px": 1520},
    layers=[
        {"index": 0, "type": "solid-color", "hex_color": "#0ea5e9"},
        {
            "index": 1,
            "type": "image",
            "file": {"type": "base64", "name": "waves.svg", "base64": wave_svg_base64},
            "position": {"x_in_px": 0, "y_in_px": 0},
            "dimensions": {"width_in_px": 2540, "height_in_px": 1520},
            "opacity": 10,
        },
    ],
)
package main

import (
    "encoding/base64"
    il "github.com/iterationlayer/sdk-go"
)

func main() {
    client := il.NewClient("YOUR_API_KEY")

    waveSvgBase64 := base64.StdEncoding.EncodeToString(generateWaveSvg("marketing-slides", 2540, 1520))
    opacity := 10

    result, _ := client.GenerateImage(il.GenerateImageRequest{
        Dimensions: il.Dimensions{WidthInPx: 2540, HeightInPx: 1520},
        Layers: []il.Layer{
            il.NewSolidColorBackgroundLayer(0, "#0ea5e9"),
            il.ImageLayer{
                Type:  "image",
                Index: 1,
                File:  il.FileInput{Type: "base64", Name: "waves.svg", Base64: waveSvgBase64},
                Position:   &il.Position{XInPx: 0, YInPx: 0},
                Dimensions: &il.Dimensions{WidthInPx: 2540, HeightInPx: 1520},
                Opacity:    &opacity,
            },
        },
    })
    _ = result
}

Same input, same output. The slides are byte-identical across runs.

Why Not Figma

A few reasons:

The tradeoff is that design iteration is slower. You can’t drag a box and see the result instantly. But for slides that follow a template — which most marketing slides do — the code-first approach is faster after the initial setup.

Try It

The Iteration Layer Image Generation API covers all layer types including layouts. The Generate Product Slide recipe shows a complete slide with pills, feature lists, and a CTA button.

If you’re generating images that follow a pattern — slides, social cards, certificates, tickets — layout layers replace the pixel math with structured composition. Define the structure once, swap the data.

Related reading

Learn how to turn the same pattern into production-ready document, image, and automation workflows.

Try with your own data

Start the trial and run this in minutes.