Claude-code-plugins-plus-skills ideogram-webhooks-events
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/ideogram-pack/skills/ideogram-webhooks-events" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-ideogram-webhooks-events && rm -rf "$T"
manifest:
plugins/saas-packs/ideogram-pack/skills/ideogram-webhooks-events/SKILL.mdsource content
Ideogram Events & Async Patterns
Overview
Ideogram's API is synchronous -- each call blocks until the image is generated (5-15 seconds). For production applications, wrap it in async patterns: job queues for batch generation, callbacks for downstream processing, and pipelines for image post-processing. This skill covers BullMQ queue patterns, callback handlers, and asset processing pipelines.
Prerequisites
configuredIDEOGRAM_API_KEY- Redis for BullMQ job queue
- Storage for generated images (S3, GCS, or R2)
- Understanding of Ideogram models and style types
Instructions
Step 1: Job Queue for Async Generation
import { Queue, Worker } from "bullmq"; import { writeFileSync, mkdirSync } from "fs"; import { join } from "path"; interface GenerationJob { prompt: string; style: string; aspectRatio: string; model: string; callbackUrl?: string; metadata?: Record<string, string>; } const connection = { host: "localhost", port: 6379 }; const imageQueue = new Queue("ideogram-generation", { connection }); // Enqueue a generation job async function submitGeneration(job: GenerationJob) { return imageQueue.add("generate", job, { attempts: 3, backoff: { type: "exponential", delay: 2000 }, removeOnComplete: 100, removeOnFail: 50, }); } // Worker processes jobs with concurrency limit const worker = new Worker("ideogram-generation", async (job) => { const { prompt, style, aspectRatio, model, callbackUrl, metadata } = job.data; // Call Ideogram API (synchronous, blocks 5-15s) const response = await fetch("https://api.ideogram.ai/generate", { method: "POST", headers: { "Api-Key": process.env.IDEOGRAM_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ image_request: { prompt, model: model || "V_2", style_type: style || "AUTO", aspect_ratio: aspectRatio || "ASPECT_1_1", magic_prompt_option: "AUTO", }, }), }); if (response.status === 429) { throw new Error("Rate limited"); // BullMQ will retry with backoff } if (!response.ok) { throw new Error(`Ideogram API error: ${response.status}`); } const result = await response.json(); const image = result.data[0]; // Download immediately (URLs expire) const imgResp = await fetch(image.url); const buffer = Buffer.from(await imgResp.arrayBuffer()); const outputDir = "./generated"; mkdirSync(outputDir, { recursive: true }); const filePath = join(outputDir, `${image.seed}.png`); writeFileSync(filePath, buffer); // Fire callback if provided if (callbackUrl) { await fetch(callbackUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ event: "generation.completed", jobId: job.id, prompt, seed: image.seed, resolution: image.resolution, filePath, metadata, }), }); } return { seed: image.seed, filePath, resolution: image.resolution }; }, { connection, concurrency: 5, // Stay under 10 in-flight limit }); worker.on("failed", (job, err) => { console.error(`Job ${job?.id} failed:`, err.message); });
Step 2: Callback Handler
import express from "express"; const app = express(); app.use(express.json()); app.post("/callbacks/ideogram", async (req, res) => { const { event, jobId, seed, filePath, metadata } = req.body; res.status(200).json({ received: true }); switch (event) { case "generation.completed": console.log(`Image generated: seed=${seed}, path=${filePath}`); await processImage(filePath, metadata); break; case "generation.failed": console.error(`Generation failed: job=${jobId}`); await notifyFailure(jobId, req.body.error); break; } });
Step 3: Batch Marketing Asset Generation
async function generateMarketingCampaign( campaignName: string, products: string[], formats: Array<{ name: string; aspect: string; style: string }> ) { const jobs = []; for (const product of products) { for (const format of formats) { const job = await submitGeneration({ prompt: `${product}, professional ${format.name} design, high quality`, style: format.style, aspectRatio: format.aspect, model: "V_2", callbackUrl: "https://api.myapp.com/callbacks/ideogram", metadata: { campaign: campaignName, product, format: format.name }, }); jobs.push(job); } } console.log(`Submitted ${jobs.length} generation jobs for campaign: ${campaignName}`); return jobs.map(j => j.id); } // Example: Generate all assets for a product launch await generateMarketingCampaign("Q1 Launch", [ "Cloud analytics dashboard", "Mobile payment app", ], [ { name: "social-square", aspect: "ASPECT_1_1", style: "DESIGN" }, { name: "story-vertical", aspect: "ASPECT_9_16", style: "DESIGN" }, { name: "blog-hero", aspect: "ASPECT_16_9", style: "REALISTIC" }, ]);
Step 4: Image Post-Processing Pipeline
import sharp from "sharp"; async function processImage(filePath: string, metadata?: Record<string, string>) { const variants = [ { suffix: "-og", width: 1200, height: 630 }, // Open Graph { suffix: "-thumb", width: 400, height: 400 }, // Thumbnail { suffix: "-social", width: 1080, height: 1080 }, // Instagram ]; for (const variant of variants) { const outputPath = filePath.replace(".png", `${variant.suffix}.webp`); await sharp(filePath) .resize(variant.width, variant.height, { fit: "cover" }) .webp({ quality: 85 }) .toFile(outputPath); console.log(`Created variant: ${outputPath}`); } }
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Rate limited | Too many concurrent jobs | Set worker concurrency to 5 |
| Content filtered | Prompt violates policy | Log and skip, notify reviewer |
| Expired URL | Worker too slow | Download in same worker step |
| Queue stalled | Redis connection lost | Configure BullMQ connection retry |
| Callback fails | Downstream service down | Fire-and-forget with retry queue |
Output
- BullMQ job queue for async generation
- Callback handler for downstream processing
- Batch generation for marketing campaigns
- Post-processing pipeline with sharp
Resources
Next Steps
For performance optimization, see
ideogram-performance-tuning.