Ideogram webhooks events
'Build event-driven workflows around Ideogram''s synchronous API.From its SKILL.md
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill ideogram-webhooks-eventsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.2 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
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
IDEOGRAM_API_KEYconfigured- 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.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.