Imgen API v2
Introduction
The Imgen API provides AI-powered image generation with multiple diffusion models, built-in character LoRA support, prompt enhancement, content compliance checking, content credentials, and watermarking. All prompt parsing, workflow construction, and post-processing are handled server-side — clients submit raw prompts and receive signed images hosted on S3.
- Multiple diffusion models with per-model style presets
- Optional LLM-powered prompt enhancement
- Automated content compliance review
- Configurable output format (JPEG, PNG, WebP, AVIF) with branding watermark
- C2PA manifest signing, invisible watermark embedding, and PDQ fingerprinting
- S3-hosted output with pre-signed URLs for the signed image and thumbnail
Base URL
All endpoints accept POST requests with a Content-Type: application/json body containing an action field.
POST https://imgen-stage.api.efficientstack.com/api/v2
Content-Type: application/json
Authorization: Bearer <API-KEY>
{
"action": "generate",
...
}
Authentication
All requests must include a Bearer token in the Authorization header:
Authorization: Bearer <API-KEY>
API keys are managed through the admin panel. Each key tracks request counts and can be enabled or disabled. Requests without a valid, enabled key receive a 401 Unauthorized response.
Prompt Syntax
The API uses a unified raw prompt format. The server parses the following tokens:
| Token | Syntax | Example | Description |
|---|---|---|---|
| Character | @name | @aya | Activates a character LoRA. One per prompt. |
| Negative | -term | -red dress | Comma-separated term prefixed with - is moved to the negative prompt. |
| Positive | everything else | black lingerie, on bed | Descriptive prompt text. |
Example
@aya black lingerie, sitting on bed, looking_at_viewer, warm lighting, -red dress, -ugly hands
Parsed as: character=aya, positive=black lingerie, sitting on bed, looking_at_viewer, warm lighting, negative=red dress, ugly hands.
Endpoints
| Action | Description |
|---|---|
models | List available characters, models, and style presets |
generate | Submit an image generation job |
status | Poll a generation job for completion |
optimize | Enhance a prompt with LLM |
random | Get a random prompt |
POST models
Returns available characters, models, and styles. No parameters beyond action.
{ "action": "models" }
Response
{
"characters": ["aaliyah", "aya", "bella", "..."],
"models": {
"eros": {
"label": "Eros",
"default_style": "professional",
"styles": {
"professional": { "label": "Professional" },
"boudoir": { "label": "Boudoir" },
"..."
}
}
}
}
POST generate
Submit an image generation job. Returns a job ID for polling.
Request Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
action | string | yes | — | "generate" |
prompt | string | yes | — | Raw prompt. Supports @character and -negative. |
model | string | no | "eros" | Diffusion model key. |
negative | string | no | "" | Additional negatives. |
optimize | boolean | no | false | LLM-enhance the prompt before generation. |
width | integer | no | 1344 | Image width. Must be a valid pair. |
height | integer | no | 768 | Image height. Must be a valid pair. |
style | string | no | model default | Style preset key for the selected model. |
format | string | no | "jpeg" | jpeg, png, webp, avif. |
quality | integer | no | 95 | Compression quality (1–100). |
lossless | boolean | no | false | Lossless compression (WebP/AVIF). |
wm_image | string | no | "" | Watermark as base64 data URI. |
wm_position | string | no | "bottom-right" | Watermark anchor. |
wm_scale | integer | no | 5 | Watermark scale (1–100). |
wm_transparency | integer | no | 100 | Watermark opacity (0–100). |
wm_rotation | integer | no | 0 | Rotation degrees (-360–360). |
wm_padding_x | integer | no | 10 | Horizontal padding (0–500). |
wm_padding_y | integer | no | 10 | Vertical padding (0–500). |
cs_author | string | no | "" | Override C2PA author. |
cs_title | string | no | "" | Override C2PA title. |
cs_description | string | no | "" | Override C2PA description. |
cs_organization | string | no | "" | Override C2PA organization. |
cs_vendor | string | no | "" | Override C2PA vendor. |
Valid Dimensions
1024×1024 (1:1), 1344×768 (16:9), 768×1344 (9:16), 1216×832 (3:2), 832×1216 (2:3), 1152×896 (4:3), 896×1152 (3:4). Invalid pairs fall back to 1344×768.
Response
{
"id": "run_abc123xyz",
"prompt": "@aya enhanced prompt text, -red dress",
"model": "eros",
"format": "jpeg",
"gen_id": "a1b2c3d4e5f6g7h8i9j0"
}
POST status
Poll a generation job. Pass the id, prompt, and model returned by generate.
{
"action": "status",
"id": "run_abc123xyz",
"prompt": "@aya enhanced prompt text, -red dress",
"model": "eros"
}
Pending
{ "status": "IN_QUEUE" }
{ "status": "IN_PROGRESS" }
Completed (Safe)
{
"status": "COMPLETED",
"meta": {
"id": "run_abc123xyz",
"gen_id": "a1b2c3d4e5f6g7h8i9j0",
"time": "2025-03-29T20:02:50Z",
"prompt": "@aya enhanced prompt, -red dress",
"positive": "photo of aya, enhanced prompt, quality tags",
"negative": "base negatives, style negatives, red dress",
"character": "aya",
"model": "eros",
"style": "professional",
"width": 1344,
"height": 768,
"format": "jpeg",
"quality": 95,
"lossless": false,
"optimized": true,
"compliance": "safe",
"compliance_codes": [],
"fingerprint": "a4f8e2c1b9d0...",
"images": [
{ "url": "https://s3...signed.jpg?...", "filename": "genid_fingerprint_00001.jpg", "type": "signed" },
{ "url": "https://s3...thumb.jpg?...", "filename": "genid_fingerprint_00001_thumb.jpg", "type": "thumbnail" }
],
"enable_invisimark": true,
"enable_c2pa": true,
"compute_pdq": true,
"wm_position": "bottom-right",
"wm_scale": 5,
"wm_transparency": 100,
"cs_author": "Author",
"cs_title": "AI Image",
"..."
}
}
Completed (Blocked)
{
"status": "COMPLETED",
"compliance": "unsafe",
"error": "Content blocked — flagged for: Category Name.",
"filter_categories": ["id1", "id2"]
}
Failed
{ "status": "FAILED", "error": "Generation failed" }
POST optimize
Enhance a prompt independently of generation.
{
"action": "optimize",
"prompt": "@aya black lingerie, on bed, -red dress",
"model": "eros"
}
{
"prompt": "@aya elegant black lace lingerie, perched on bed, warm lighting, -red dress"
}
POST random
Returns a random prompt. Omit character for random, set empty "" for none, or specify a name.
{ "action": "random" }
{ "action": "random", "character": "aya" }
{ "action": "random", "character": "" }
{ "prompt": "@aya black lace lingerie, sitting on bed, warm lighting" }
Prompt Construction
Positive Prompt
[trigger], [character_positive], {user prompt}[, style quality tags]
Negative Prompt
{base negatives}[, style negatives][, character negatives][, parsed -terms][, explicit negative]
Content Credentials
All generated images include C2PA manifest signing, invisible watermark embedding, and PDQ fingerprinting. The signed image and thumbnail are delivered as pre-signed S3 URLs in meta.images.
Override C2PA metadata per request: cs_author, cs_title, cs_description, cs_organization, cs_vendor.
Polling Strategy
| Setting | Recommended |
|---|---|
| Interval | 3 seconds |
| Max polls | 120 (6 min timeout) |
| Overlap guard | Wait for each poll to complete before starting the next |
Terminal states: COMPLETED, FAILED, TIMED_OUT, CANCELLED. On COMPLETED, always check compliance before accessing meta.
Error Handling
| Status | Error | Cause |
|---|---|---|
| 400 | Missing action | No action field |
| 400 | Prompt is required | Empty/missing prompt |
| 400 | Invalid character | Unknown @name |
| 400 | Bad ID | Invalid job ID |
| 401 | Unauthorized | Missing or invalid API key |
| 502 | Generation failed | Upstream error |
| 502 | Enhancement failed | LLM error |
Silent fallbacks: invalid model → first model, invalid dimensions → 1344×768, invalid style → model default, invalid format → jpeg.
Code Examples
const API = 'https://imgen-stage.api.efficientstack.com/api/v2';
const TOKEN = 'your-api-key';
async function api(body) {
const r = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${TOKEN}` },
body: JSON.stringify(body)
});
return r.json();
}
async function generateImage(prompt, opts = {}) {
const job = await api({
action: 'generate', prompt, model: opts.model || 'eros',
optimize: opts.optimize || false, width: opts.width || 1344,
height: opts.height || 768, style: opts.style || '', format: opts.format || 'jpeg'
});
if (job.error) throw new Error(job.error);
for (let i = 0; i < 120; i++) {
await new Promise(r => setTimeout(r, 3000));
const s = await api({ action: 'status', id: job.id, prompt: job.prompt, model: job.model });
if (s.status === 'IN_QUEUE' || s.status === 'IN_PROGRESS') continue;
if (s.status === 'COMPLETED') {
if (s.compliance === 'unsafe') throw new Error(s.error);
return s.meta;
}
throw new Error(s.error || 'Failed: ' + s.status);
}
throw new Error('Timeout');
}
const meta = await generateImage('@aya black lingerie, warm lighting', { style: 'professional' });
const signed = meta.images.find(i => i.type === 'signed');
console.log('Image URL:', signed?.url);
import requests, time
API = "https://imgen-stage.api.efficientstack.com/api/v2"
TOKEN = "your-api-key"
def api(body):
return requests.post(API, json=body,
headers={"Authorization": f"Bearer {TOKEN}"}).json()
def generate(prompt, model="eros", style="", fmt="jpeg"):
job = api({"action":"generate","prompt":prompt,"model":model,"style":style,"format":fmt})
if "error" in job: raise Exception(job["error"])
for _ in range(120):
time.sleep(3)
s = api({"action":"status","id":job["id"],"prompt":job["prompt"],"model":job["model"]})
if s["status"] in ("IN_QUEUE","IN_PROGRESS"): continue
if s["status"] == "COMPLETED":
if s.get("compliance") == "unsafe": raise Exception(s.get("error"))
return s["meta"]
raise Exception(s.get("error", f"Failed: {s['status']}"))
raise TimeoutError("Polling timed out")
meta = generate("@aya black lingerie, warm lighting", style="professional")
signed = next(i for i in meta["images"] if i["type"] == "signed")
print("URL:", signed["url"])
TOKEN="your-api-key"
BASE="https://imgen-stage.api.efficientstack.com/api/v2"
# List models
curl -s -X POST "$BASE" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"models"}'
# Generate
curl -s -X POST "$BASE" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"generate","prompt":"@aya black lingerie","model":"eros","style":"professional"}'
# Poll status (replace JOB_ID)
curl -s -X POST "$BASE" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"status","id":"JOB_ID","prompt":"@aya ...","model":"eros"}'
<?php
$api = 'https://imgen-stage.api.efficientstack.com/api/v2';
$token = 'your-api-key';
function apiCall($api, $token, $body) {
$ch = curl_init($api);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', "Authorization: Bearer $token"],
]);
$r = curl_exec($ch); curl_close($ch);
return json_decode($r, true);
}
$job = apiCall($api, $token, ['action'=>'generate','prompt'=>'@aya black lingerie','model'=>'eros']);
for ($i = 0; $i < 120; $i++) {
sleep(3);
$s = apiCall($api, $token, ['action'=>'status','id'=>$job['id'],'prompt'=>$job['prompt'],'model'=>$job['model']]);
if (in_array($s['status'], ['IN_QUEUE','IN_PROGRESS'])) continue;
if ($s['status'] === 'COMPLETED') {
if (($s['compliance'] ?? '') === 'unsafe') die($s['error']);
$signed = array_values(array_filter($s['meta']['images'], fn($i) => $i['type']==='signed'))[0];
echo "URL: " . $signed['url']; break;
}
die($s['error'] ?? 'Failed');
}