From Raw Invoice to Branded Social Card — One Pipeline, Zero Glue Code

7 min read

Documents Go In, Visuals Come Out

You have a process that starts with a document and ends with a visual. An invoice that needs to become a payment summary card for the finance dashboard. A contract that needs to become a deal-closed announcement for Slack. A report that needs to become a visual summary for the executive deck.

The typical approach: write a PDF parser, extract the fields with regex or templates, pass the data to a rendering service, debug the layout, deploy the whole thing on a server. Weeks of work for a pipeline that breaks every time a document format changes.

There’s a simpler way. The Document Extraction API extracts structured data from any document. The Image Generation API renders that data into a branded visual. Two API calls. The document goes in, the visual comes out.

No servers. No templates. No regex. No glue code.

The Pipeline

Let’s build a concrete example: an invoice arrives, and we produce a branded summary card showing the vendor name, invoice total, date, and line items. The kind of card you’d post in a Slack channel or embed in a finance dashboard.

Step 1: Parse the Invoice

Send the invoice to the Document Extraction API with a schema that describes the fields you need:

import { IterationLayer } from "iterationlayer";

const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const parseResult = await client.extract({
  files: [
    { type: "url", name: "invoice.pdf", url: "https://example.com/invoices/2026-0847.pdf" },
  ],
  schema: {
    fields: [
      { name: "vendor_name", type: "TEXT", description: "The name of the vendor or supplier" },
      { name: "invoice_date", type: "DATE", description: "The date the invoice was issued" },
      { name: "total_amount", type: "CURRENCY_AMOUNT", description: "The total amount due" },
      { name: "currency_code", type: "CURRENCY_CODE", description: "The currency of the total amount" },
      {
        name: "line_items",
        type: "ARRAY",
        description: "The line items on the invoice",
        item_schema: {
          fields: [
            { name: "description", type: "TEXT", description: "Line item description" },
            { name: "amount", type: "CURRENCY_AMOUNT", description: "Line item amount" },
          ],
        },
      },
    ],
  },
});

The response comes back as structured JSON with confidence scores per field:

{
  "success": true,
  "data": {
    "results": [
      {
        "vendor_name": { "value": "Acme Supplies Ltd.", "confidence": 0.96 },
        "invoice_date": { "value": "2026-02-15", "confidence": 0.98 },
        "total_amount": { "value": 2090.00, "confidence": 0.97 },
        "currency_code": { "value": "USD", "confidence": 0.99 },
        "line_items": {
          "value": [
            [
              { "value": "Office chairs (x4)", "confidence": 0.94 },
              { "value": 1200.00, "confidence": 0.96 }
            ],
            [
              { "value": "Standing desks (x2)", "confidence": 0.93 },
              { "value": 890.00, "confidence": 0.95 }
            ]
          ],
          "confidence": 0.94
        }
      }
    ]
  }
}

Field types like TEXT, DATE, CURRENCY_AMOUNT, CURRENCY_CODE, ARRAY, and more. The parser handles PDF layout analysis, OCR for scanned documents, and field extraction automatically. You just describe what you want.

Step 2: Generate the Summary Card

Now take that extracted data and render it as a branded image using the Image Generation API:

const { results: [invoiceData] } = parseResult.data;

const lineItemSummary = invoiceData.line_items.value
  .map(([description, amount]) => `${description.value}  —  $${amount.value.toFixed(2)}`)
  .join("\n");

const summaryCard = await client.generateImage({
  width_in_px: 800,
  height_in_px: 500,
  output_format: "png",
  layers: [
    {
      type: "solid-color-background",
      index: 0,
      hex_color: "#1a1a2e",
    },
    {
      type: "rectangle",
      index: 1,
      x_in_px: 0,
      y_in_px: 0,
      width_in_px: 800,
      height_in_px: 4,
      hex_color: "#4ade80",
    },
    {
      type: "text",
      index: 2,
      text: invoiceData.vendor_name.value,
      font_name: "Inter",
      font_weight: "Bold",
      font_size_in_px: 28,
      hex_color: "#ffffff",
      x_in_px: 40,
      y_in_px: 40,
      max_width_in_px: 720,
      max_height_in_px: 40,
    },
    {
      type: "text",
      index: 3,
      text: `**$${invoiceData.total_amount.value.toFixed(2)}** ${invoiceData.currency_code.value}`,
      font_name: "Inter",
      font_weight: "Regular",
      font_size_in_px: 48,
      hex_color: "#4ade80",
      x_in_px: 40,
      y_in_px: 100,
      max_width_in_px: 720,
      max_height_in_px: 60,
    },
    {
      type: "text",
      index: 4,
      text: invoiceData.invoice_date.value,
      font_name: "Inter",
      font_weight: "Regular",
      font_size_in_px: 16,
      hex_color: "#94a3b8",
      x_in_px: 40,
      y_in_px: 170,
      max_width_in_px: 720,
      max_height_in_px: 24,
    },
    {
      type: "rectangle",
      index: 5,
      x_in_px: 40,
      y_in_px: 210,
      width_in_px: 720,
      height_in_px: 1,
      hex_color: "#334155",
    },
    {
      type: "text",
      index: 6,
      text: lineItemSummary,
      font_name: "Inter",
      font_weight: "Regular",
      font_size_in_px: 16,
      hex_color: "#cbd5e1",
      x_in_px: 40,
      y_in_px: 230,
      max_width_in_px: 720,
      max_height_in_px: 200,
    },
  ],
  fonts: [
    {
      name: "Inter",
      weight: "Regular",
      style: "normal",
      file: { type: "url", name: "Inter-Regular.ttf", url: "https://example.com/fonts/Inter-Regular.ttf" },
    },
    {
      name: "Inter",
      weight: "Bold",
      style: "normal",
      file: { type: "url", name: "Inter-Bold.ttf", url: "https://example.com/fonts/Inter-Bold.ttf" },
    },
  ],
});

The result is an 800x500 branded card with a dark background, green accent, the vendor name, the total amount in large text, the date, a divider, and the line items. Professional. Branded. Generated from raw document data.

The Pattern Generalizes

This isn’t just for invoices. The same two-step pattern — parse, then render — works for any document-to-visual workflow.

Contracts to deal cards. Parse the contract for client name, deal value, effective date, and key terms. Render a “deal closed” card for Slack or the CRM dashboard.

Receipts to expense summaries. Parse receipts for merchant name, amount, date, and category. Render a visual expense summary card for reporting.

Reports to executive summaries. Parse a report for KPIs, metrics, and conclusions. Render a visual one-pager that distills the report into a shareable image.

Articles to social cards. Parse an article for the title, author, publication, and key quote. Render an Open Graph card or social media image for sharing.

In each case, the Document Extraction handles the messy part — understanding document layouts, extracting structured data from unstructured content, handling OCR for scanned documents. The Image Generation API handles the visual part — composing layers, rendering text, producing a polished output.

Why Not Just Use a Template Engine?

Template engines like Puppeteer-based HTML-to-image tools work for simple cases. They break down when you need:

  • Precise pixel control. HTML rendering is approximate. CSS font-size: 48px doesn’t guarantee 48 physical pixels. The Image Generation API uses exact pixel coordinates — what you specify is what you get.
  • Custom fonts without a server. Loading web fonts in Puppeteer requires a headless browser, which requires a server. The Image Generation API accepts font files in the request.
  • No infrastructure. Puppeteer needs a Node.js server with Chromium installed. That’s a Docker image, a deployment, and a maintenance burden. The API is a POST request.

The tradeoff is flexibility. HTML templates can lay out anything. The Image Generation API uses a layer model — backgrounds, rectangles, text, and images. For structured visuals like summary cards, report cards, and social images, the layer model is more than enough. For full-page document rendering, use a template engine.

Error Handling and Confidence

The Document Extraction returns confidence scores between 0.0 and 1.0 for every extracted field. In a production pipeline, check these before rendering:

const MINIMUM_CONFIDENCE = 0.8;

const isHighConfidence = (field: { confidence: number }): boolean =>
  field.confidence >= MINIMUM_CONFIDENCE;

const hasReliableData = isHighConfidence(invoiceData.vendor_name)
  && isHighConfidence(invoiceData.total_amount)
  && isHighConfidence(invoiceData.invoice_date);

If any critical field falls below your threshold, flag the document for manual review instead of rendering a card with potentially wrong data. The confidence scores make this decision automatic.

Get Started

Check the full API references — Document Extraction and Image Generation. Both have TypeScript and Python SDKs.

Sign up for a free account — no credit card required. Parse a document, grab the data, and generate your first visual. The pipeline is two API calls, and it works in any language that can make HTTP requests.

Start building in minutes

Free trial included. No credit card required.