Generate Images
Image Generation lets a deployed agent turn a prompt into one or more images. Agent steps call the typed ctx.sapiom.contentGeneration.images capability; Sapiom supplies the authenticated cloud connection when the deployed agent runs.
Choose the generation pattern
Section titled “Choose the generation pattern”| What the agent needs | Recommended operation or input |
|---|---|
| Generate and wait in the current step | contentGeneration.images.create(...) |
| Suspend the workflow while generation finishes | images.launch(...) plus pauseUntilSignal(...) |
| More than one image | Set count |
| A durable private asset | storage: { visibility: "private" } |
| An asset intended for public sharing | storage: { visibility: "public" } |
| A video rather than an image | See Generate Video |
Use create for an ordinary blocking step. Use launch when the workflow should pause and resume on the completed generation instead of holding the launching step open.
Generate and persist images
Section titled “Generate and persist images”const generation = await ctx.sapiom.contentGeneration.images.create({ prompt: "Editorial illustration of a solar-powered city block at sunrise, clean geometric forms", aspectRatio: "16:9", count: 2, storage: { visibility: "private" },});
const images = generation.images ?? [];if (images.length === 0) { throw new Error("Image generation returned no images");}
return { resolvedModel: generation.resolvedModel, images: images.map((image) => { if (image.storageError) { throw new Error(`Image persistence failed: ${image.storageError}`); }
return { fileId: image.fileId ?? null, temporaryUrl: image.url, width: image.width ?? null, height: image.height ?? null, }; }),};The result includes the resolvedModel and an optional images array. Each image has a provider-hosted url and can include dimensions and content type. Treat the provider URL as temporary.
When storage succeeds, retain fileId as the durable reference. A returned downloadUrl is convenient but short-lived; mint a fresh one later with ctx.sapiom.fileStorage.getDownloadUrl(fileId). Persistence is per output, so inspect storageError on every image rather than assuming the whole batch stored successfully.
Control the output without pinning a provider
Section titled “Control the output without pinning a provider”Omit model unless the agent needs a specific Sapiom routing alias. Prefer the neutral fields—aspectRatio, count, seed, negativePrompt, referenceImage, and outputFormat—over provider-specific passthrough values. A field unsupported by the selected model is rejected rather than silently ignored.
Generation is not guaranteed to be visually identical across models or service updates. Use a deterministic seed only where the selected model supports it, and test downstream code against the returned shape rather than pixel identity.
Dispatch generation
Section titled “Dispatch generation”images.launch returns a handle with a request ID, resolved model, wait(), and the signal metadata required by pauseUntilSignal. The signal is IMAGE_RESULT_SIGNAL and the resumed step receives an ImageResultPayload; import both from @sapiom/tools.
Declare the static pause edge on the launching step. Both signal and resumeStep are required, and the declaration is what allows that step’s run to return a pause directive:
import { defineStep, pauseUntilSignal, terminate, type AgentExecutionContext,} from "@sapiom/agent";import { IMAGE_RESULT_SIGNAL, type ImageResultPayload } from "@sapiom/tools";
const generateHero = defineStep({ name: "generate-hero", pause: { signal: IMAGE_RESULT_SIGNAL, resumeStep: "use-image" }, async run(input: { prompt: string }, ctx: AgentExecutionContext) { const handle = await ctx.sapiom.contentGeneration.images.launch({ prompt: input.prompt, storage: { visibility: "private" }, });
return pauseUntilSignal(handle, { resumeStep: "use-image" }); },});
const useImage = defineStep({ name: "use-image", terminal: true, async run(result: ImageResultPayload, ctx: AgentExecutionContext) { const output = result.outputs[0]; if (!output?.fileId) { throw new Error("Resumed without a stored image"); }
return terminate({ fileId: output.fileId }); },});The resume target declares no pause of its own — it receives the payload as its input. That payload is an outputs array plus the generation’s resolvedModel, not the live handle or the same images wrapper returned by create(), and it carries one entry per generated image. Import ImageResultPayload instead of hand-writing that shape.
A downloadUrl on a resumed output may already have expired, since the step can resume long after the URL was minted. Re-fetch from fileId rather than treating a missing or stale URL as a missing asset.
handle.wait() is the alternative to pausing: it polls inline and resolves the same result create would, accepting timeoutMs and pollMs (2 minutes and 2 seconds by default). A generation that fails is not currently distinguishable from a slow one on that path — the call surfaces the timeout error either way — so prefer the pause and resume path for long generations, where the completion signal drives the resume instead of a deadline.
Test the behavior locally
Section titled “Test the behavior locally”Local Run replaces image generation with deterministic data and does not generate or store an image. Override the exact path when the step requires a stored output or a particular failure:
{ "version": 1, "steps": { "generate-hero": { "contentGeneration.images.create": { "images": [ { "url": "https://fixtures.example/hero.png", "contentType": "image/png", "width": 1280, "height": 720, "fileId": "file-hero-local", "downloadUrl": "https://fixtures.example/hero-download", "downloadUrlExpiresAt": "2099-01-01T00:00:00.000Z" } ], "resolvedModel": "stub-model" } } }}contentGeneration.images.launch can use its own override or the shared create result. After the run, assert the terminal output and require both unusedStubs and stubWarnings to be empty. A passing fixture does not prove live model availability, visual quality, storage, latency, or usage.
Manage usage and retention
Section titled “Manage usage and retention”Image count, model route, dimensions, and persistence can affect runtime usage. Generate only the variants the decision needs, avoid retrying a satisfactory output, and request storage only when the result must survive the provider URL.
Use the signed-in capability catalog for current models, limits, and pricing. Do not copy a model inventory or fixed rate into the agent.
© 2026 Sapiom, Inc.