Skip to Content
AgentsBlog Image Generator

Blog Image Generator

[To Build] · agent__blog-image-generator · Azure OpenAI GPT Image (DALL-E 3)

Generates brand-matched hero and inline section images for blog posts. Uses the post title, primary keyword, tenant brand colours, and chosen image style to produce images, then resizes and uploads them to DigitalOcean Spaces, embedding the URLs directly into the blog post Markdown.

Related: Blog Image Generation · Blog Writer · Social Post Designer


Overview

FunctionGenerate and embed hero + inline images for blog posts
TypeWorker — Content
ModelAzure OpenAI GPT Image (DALL-E 3)
Queueagent__blog-image-generator
Concurrency2
Timeout5 min
Est. cost / task~$0.08 hero · ~$0.04 inline (Azure standard quality)
Credits0.5 cr hero image · 0.25 cr per inline image
PlanPro+

Triggers

Trigger typeWhenWho initiates
ManualDM clicks “Generate Images” on blog detail pageTenant admin / DM reviewer
AutomaticBlogPost.status transitions to published and tenant has auto_generate_hero_image = trueActivity Planner

Input

interface BlogImageGeneratorInput { tenantId: string; blogPostId: string; imageType: 'hero' | 'inline_section'; sectionTitle?: string; // required for inline_section postTitle: string; primaryKeyword: string; imageStyle: ImageStyle; primaryColor?: string; // hex e.g. "#1A73E8" — from BrandAsset secondaryColor?: string; } type ImageStyle = | 'photo_realistic' | 'flat_illustration' | 'minimal_infographic' | 'abstract';

Output

interface BlogImageGeneratorOutput { tenantId: string; blogPostId: string; imageType: 'hero' | 'inline_section'; sectionTitle?: string; imageUrl: string; // DigitalOcean Spaces public URL altText: string; // auto-generated for accessibility width: number; height: number; }

How It Works

  1. Constructs an image prompt from the post title, primary keyword, image style, and brand colours
  2. Calls Azure OpenAI Images.generate with model: dall-e-3, size: 1024x1024, quality: standard
  3. Downloads the generated image
  4. Resizes using sharp:
    • Hero: 1200 × 630px
    • Inline section: 800 × 450px
  5. Uploads to DigitalOcean Spaces at path tenants/{tenantId}/blog/{blogPostId}/{imageType}-{timestamp}.jpg
  6. Generates descriptive alt text from the post title and section context
  7. Returns { imageUrl, altText, width, height }
  8. API layer inserts image into BlogPost.bodyMarkdown:
    • Hero: prepended as ![{altText}]({imageUrl})\n\n
    • Inline: inserted after the target H2 as \n\n![{altText}]({imageUrl})\n\n
  9. If post is already published to WordPress, syncs: hero → set as featured_media; inline → update WP post content

Image Prompt Construction

const styleDescriptions: Record<ImageStyle, string> = { photo_realistic: 'Professional photorealistic image, high quality photography, commercial aesthetic, no text overlays', flat_illustration: 'Modern flat design illustration, clean lines, simple shapes, minimal shadows, digital art style, no text', minimal_infographic: 'Simple clean diagram with minimal icons and shapes, white background, minimal colour palette, no text labels', abstract: 'Abstract geometric composition, smooth gradients, modern and sophisticated, no text or recognizable objects', }; function buildImagePrompt(input: BlogImageGeneratorInput): string { const styleDesc = styleDescriptions[input.imageStyle]; const colorInstruction = (input.primaryColor && input.secondaryColor) ? `Use ${input.primaryColor} as the dominant colour and ${input.secondaryColor} as an accent.` : input.primaryColor ? `Dominant colour: ${input.primaryColor}.` : ''; const subject = input.imageType === 'hero' ? `Representing the concept of: ${input.postTitle}. Related to: ${input.primaryKeyword}.` : `Illustrating the section: "${input.sectionTitle}". Context: ${input.primaryKeyword}.`; return [styleDesc, colorInstruction, subject, '16:9 wide banner format. No text overlays.'] .filter(Boolean) .join(' '); }

RAG Usage

None. The blog image generator does not query RAG. It relies entirely on the post metadata (title, keyword, section title) and brand asset data passed directly in the input.


HITL Gates

None. Image generation is a production step after the blog post has already been approved. The DM can regenerate or reject individual images from the dashboard without a formal approval gate.


Guardrails

RuleEnforcement
Never generate images with text overlaysPrompt explicitly instructs “no text overlays”; content policy check on Azure side
Never generate images with recognisable real peoplePrompt uses abstract/conceptual subjects only; real person references stripped from post title before prompt construction
Image must be resized before uploadsharp resize step is mandatory; raw 1024×1024 is never uploaded
Alt text is always populatedIf no alt text is generated, fallback: ${postTitle} — blog post image
Credit deducted before job enqueueAPI checks credit balance before enqueuing; returns 402 if insufficient

Storage Path Convention

DigitalOcean Spaces bucket: leadmetrics-media Path: tenants/{tenantId}/blog/{blogPostId}/hero-{timestamp}.jpg tenants/{tenantId}/blog/{blogPostId}/inline-{sectionSlug}-{timestamp}.jpg

All images are public-read. CDN URL format: https://cdn.leadmetrics.io/tenants/{tenantId}/blog/{blogPostId}/{filename}

© 2026 Leadmetrics — Internal use only