Images
Choose the endpoint that matches your workflow:
| Workflow | Endpoint |
|---|---|
| Generate one image or edit one source image | POST /v3/images/generate |
| Combine text/design changes, image generation, multiple-reference edits, and follow-up messages | POST /v3/templates/generate with features.ai.image |
| Reconstruct an editable template from a screenshot | POST /v3/templates/import — see Template Importer |
Generate or edit one image
POST /v3/images/generate creates one custom image. Supply an optional image to edit an existing image instead. It uses the same image service as the AI Assistant and the standalone Image Editor.
Authenticate with your project API key. Personal Access Tokens and OAuth tokens also support this endpoint with a projectId query parameter; OAuth tokens require designs:write scope. Personal Access Tokens use your workspace permissions rather than OAuth scopes. The project must have AI Assistant access and available AI credits.
curl https://api.unlayer.com/v3/images/generate \
--header "Authorization: Bearer $UNLAYER_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"prompt": "Create a watercolor illustration of a turtle swimming underwater",
"end_user_id": "customer-123"
}'
{
"operation": "image_generation",
"image": { "url": "https://assets.example/generated-image.png" }
}
To edit, include the source URL and describe the change. Prompts may be written in the user's language.
{
"prompt": "Remove the background and keep the turtle",
"image": "https://your-cdn.example/turtle.png",
"end_user_id": "customer-123"
}
The response has operation: "image_edit". Check for image before using its URL: a successful provider response may contain a text message without an image, for example when clarification is needed. There is no separate /v3/images/edit endpoint.
| Field | Purpose |
|---|---|
prompt | Required instruction, up to 20,000 characters. |
image | Optional publicly reachable image URL or PNG, JPEG, WebP, or GIF base64 data URL, at most 10 MB after decoding or downloading. Omit to generate a new image. |
background | Optional transparent or opaque. Omission keeps the existing provider behavior. Use transparent to require real alpha or opaque for a filled background. |
end_user_id | Optional stable identifier for end-user usage attribution. |
model | Optional image provider/model selection. Omit to use Unlayer's default. |
quality | Optional low, medium, or high; omission uses the selected provider's default. |
fallbackModels | Optional boolean or ordered array of image models for transient provider failures. |
model accepts a provider (openai or google), a supported model ID, or a provider/modelId pair. Leave it unset to use Unlayer’s default. Supported model IDs are gpt-image-1.5, gpt-image-2, and gemini-3.1-flash-image. The retired Gemini IDs gemini-2.5-flash-image and gemini-3.1-flash-image-preview are still accepted and run on gemini-3.1-flash-image.
When both model and fallbackModels are omitted, Unlayer may use its default fallback chain. Pinning model disables that chain unless fallbackModels is true or an ordered list. Use fallbackModels: false to disable it explicitly, or an array of up to ten models to replace it. The chain also supplies compatible models when real transparency is required. Fallback handles temporary provider failures; invalid input, unsupported models, and rejected credentials do not trigger a switch. Configured custom provider keys are selected for the provider actually called.
Transparency requires openai/gpt-image-1.5 or openai/gpt-image-2 (provider preview support). Only compatible models from your configured model/fallback chain are used; if none are available, the request returns 400 before generation. An opaque result is rejected with 422, without returning an image or charging image credits. To request a transparent new asset, pass background: "transparent"; a prompt alone does not enforce the pixel check. Validated transparent results support non-animated images up to 25 megapixels.
For OpenAI, omitted quality means medium. For Gemini, omission leaves resolution at the provider default; explicit low/medium/high requests map to 1K/2K/4K, subject to model support. Higher resolution can consume more credits.
GIF inputs use the first frame and produce still images. Both the original GIF and its first frame converted to PNG must fit the image-operation byte limit; the frame is limited to 25 megapixels.
Source URLs must return a supported image directly and be reachable without your application’s cookies or authorization headers. Use a sufficiently long-lived signed URL when the source is private. Redirect destinations must also be publicly reachable.
Returned images are temporary: copy the image to your own storage within seven days. If temporary storage is unavailable, image.url may be a data URL; store those bytes directly.
The direct endpoint returns the image or message, not a per-request usage object. Usage is reported as image_generation or image_edit in AI credits reporting. A project without access receives 403; exhausted credits return 429. Invalid input returns 400, oversized request bodies return 413, and an unavailable image provider returns 502. Provider key failures also return 502, with CUSTOM_AI_KEY_INVALID for a rejected project key or AI_PROVIDER_AUTH_FAILED for an Unlayer provider configuration issue. A valid Unlayer credential does not need to be replaced in either case. Structured provider safety refusals return 422 with IMAGE_SAFETY_REJECTED and are not retried or charged image credits. A normal provider clarification can return 200 with only message; that successful provider response consumes credits for its reported input and output usage. This endpoint does not search stock libraries.
Images in an Assistant conversation
Assistant edits preserve existing real transparency unless the prompt explicitly requests a filled background. Pure black-and-white conversions preserve the displayed orientation, dimensions and alpha mask. Assistant alpha inspection and grayscale conversion support non-animated sources up to 25 megapixels; ordinary JPEG edits do not require alpha inspection.
Use the existing POST /v3/templates/generate endpoint when a request combines design changes, text, and images. Its messages accept image parts alongside text. Use the optional features.ai.image configuration to enable image generation and editing, with the same option names as unlayer.init(). Omitting it preserves existing request behavior; older clients do not need any changes.
{
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Make image #1 black and white" },
{ "type": "image", "image": "https://your-cdn.example/portrait.png" }
]
}
],
"output": { "kind": "template", "displayMode": "email" },
"features": {
"ai": {
"image": { "generation": true, "editing": true, "upload": true }
}
},
"end_user_id": "customer-123"
}
Image numbers follow the order of image parts in the last user message. Include earlier messages to continue editing a previous result. Keep the conversation to at most ten messages and six image parts across those messages, including earlier assistant image results. Data URLs have a combined decoded limit of 10 MB. Each image operation also limits its downloaded or decoded source images to 10 MB in total. Public image URLs and PNG, JPEG, WebP, or GIF base64 data URLs are supported. A URL pasted in user text can also be selected as an edit source. The last user message must include text. Trim older history as the conversation grows.
features.ai.image option | Behavior |
|---|---|
enabled | Set to false to disable image generation, editing, and image destinations. Defaults to true when the image configuration is supplied. |
generation | Allow custom image generation. Defaults to true. |
editing | Allow editing an existing image. Defaults to true. |
upload | Allow image attachments as references, unchanged photos, or edit sources. Pasted-URL edits also require editing. Defaults to true. |
enableDesignImageTargeting | Use existing images and resolve replacement or insertion destinations from context.fullDesign. Defaults to true. Set to false to keep image results separate from design destinations. |
model, quality, fallbackModels | Configure image providers independently of the top-level conversation model. See the image options above. |
features.ai.image: {} enables the default image actions. Every option is optional. To target an existing template image, supply context.fullDesign; use context.selection to identify a selected image, or describe the image in the prompt. Without a full design, the assistant can still generate images and edit attachments. Requests for several images can return several independent results. Existing design permissions still apply.
Online image search is supported in the editor, but is not available through this public endpoint. Omit search or set it to false; explicitly requesting search: true returns 400. safeSearch only affects editor stock search. Automatic alt-text configuration (altText) belongs to the editor, which applies images to the design; API integrations supply alt text when applying results.
Combine HTML references and images
The existing POST /v3/templates/generate endpoint accepts HTML as an inline file part in a user message. Include images and the actual instruction in that same message:
const request = {
output: { kind: 'template', displayMode: 'email' },
end_user_id: 'customer-123',
context: { fullDesign: savedDesign },
features: {
ai: { image: { upload: true, generation: false, editing: false } },
},
messages: [
{
role: 'user',
content: [
{
type: 'file',
file: {
mediaType: 'text/html',
name: 'inspiration.html',
text: htmlSource,
},
},
{ type: 'image', image: 'https://your-cdn.example/product.jpg' },
{
type: 'text',
text: 'Borrow the colors from this HTML, keep my copy, and use image #1 as the hero.',
},
],
},
],
};
htmlSource is the HTML string; savedDesign is the current Unlayer design. Omit the design when creating a new template. HTML attachments require full-template AI Assistant access. The instruction determines whether the HTML is inspiration or a template to recreate; attachment presence alone is not a replacement request. Existing design permissions apply.
The file object requires mediaType: 'text/html' and non-empty text; name is optional and limited to 255 characters. Do not combine text and url in one file part. This addition is optional and leaves existing text/image request bodies unchanged. Remote HTML file URLs are not fetched through this attachment contract.
Current and retained user messages together accept at most three HTML references totaling 5 MiB of UTF-8 source. Include the earlier file parts on follow-up requests when they are still relevant, removing older references to stay within the limit. Current sources and the design must also fit the Assistant’s combined context limit after HTML cleanup. Older retained HTML that does not fit or cannot be prepared is omitted so it cannot block an unrelated follow-up. Invalid attachments return a validation error before generation.
Embedded image data and inline photo references used with HTML (or with editing: false) are hosted by the existing project-storage service before generation; ordinary image URLs are reused. Scripts and nonvisual markup are removed, and reference markup is treated as data rather than instructions. Source text and filenames are excluded from prompt tracking. These turns use normal Assistant credits, including any separately requested generative image operations. Placing an uploaded photo unchanged does not call an image generation model; the conversation itself still consumes credits.
For unchanged photos, accepted design edits appear directly in output.data. The separate images array below is for generated, edited, or searched image results.
Target an image in a design
Pass the full saved design and, when the user selected an image, its authored content ID:
const response = await fetch('https://api.unlayer.com/v3/templates/generate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.UNLAYER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'Make the selected image black and white' },
],
},
],
output: { kind: 'content', displayMode: 'email' },
features: {
ai: { image: { generation: false, editing: true } },
},
context: {
fullDesign: savedDesign,
selection: { collection: 'contents', id: 'hero-image' },
},
end_user_id: 'customer-123',
}),
});
// A 304 means no changes and has no JSON body.
if (response.status !== 304) {
const result = await response.json();
const partial = !response.ok;
if (partial && !result.images?.length) {
throw new Error(`Image edit failed: ${response.status}`);
}
// Store result.images and apply their destinations, including partial results.
// When partial is true, also show result.message so the user sees the failure.
}
Run this code on your backend. savedDesign is your full Unlayer design JSON, and hero-image must match the image block's id, not an internal editor index. To target by description without a selection, omit selection, use output.kind: 'template', and ask for the relevant image or insertion location in the prompt. Set enableDesignImageTargeting: false to use only attachments and prior results as image references, without automatic design destinations.
Handle results and follow-up turns
Image results are returned in an optional top-level images array, alongside the existing output, model, and usage fields:
{
"images": [
{
"id": "result-1",
"url": "https://assets.example/edited-image.png",
"source": "edited",
"destination": {
"kind": "replace",
"imageId": "hero-image",
"expectedSrc": "https://your-cdn.example/portrait.png"
}
}
]
}
output.data contains design changes; it does not automatically incorporate the returned images. Store each image on your CDN within seven days, then apply its optional destination to your current design. For a replacement, check that the image identified by imageId still has expectedSrc before changing it. An insertion identifies a parentId with position (start, end, before, or after) and an anchorId for relative positions. Images without a destination are previews for your application to present. For an insertion, parentId is an authored template body or column ID; anchorId is the sibling ID used by before/after. Recheck that destinations still exist and permissions still allow the operation before applying an asynchronous result. Supply content-based alt text when adding or changing an image.
When image work completes without a design change, the API returns 200 with the completed images and usage, and may omit id, model and output. Keep your current design when output is absent. A 304 has no body and is used only when there is no image work to report.
To continue an edit, append the returned image URL as an image part in an assistant message, followed by the next user instruction. Include any useful assistant reply text. Keep those URLs valid for the next request; use your stored copies after temporary URLs expire.
If the conversation fails after an image completes, the non-2xx response retains optional images and usage alongside its usual error fields. Read the JSON body before handling the error. Keep completed images and report the failure; do not automatically retry the whole request, which can generate and charge for them again. A provider response that consumes credits without producing an image can still contribute to usage. Network disconnects cannot deliver a response, so avoid long synchronous bulk requests when your client or proxy has a short timeout.
For a pure black-and-white request, the conversation can apply a grayscale conversion that preserves dimensions and transparency without an image-generation charge. Conversation and automatic image-description usage still apply.
usage.aiCreditsUsed includes both the conversation and image operations. Image generation and editing are recorded separately in usage reporting, attributed to the same end_user_id; adding their credits to the response does not charge them again. Token fields describe the conversation model, and raw provider costs are not exposed by the public API.
Authentication, AI access, and credit limits are the same as for design generation. Use POST /v3/templates/import to reconstruct a design from an existing screenshot; attaching an image here does not import its layout.