diff --git a/docs/api/image-generation-advanced.md b/docs/api/image-generation-advanced.md
new file mode 100644
index 0000000..2b5d74e
--- /dev/null
+++ b/docs/api/image-generation-advanced.md
@@ -0,0 +1,449 @@
+# Advanced Image Generation
+
+Advanced generation features: reference images, aliases, flows, and regeneration. For basic generation, see [image-generation.md](image-generation.md).
+
+All endpoints require Project Key authentication via `X-API-Key` header.
+
+---
+
+## Reference Images
+
+Use existing images as style or content references for generation.
+
+### Using References
+
+Add `referenceImages` array to your generation request:
+
+```json
+{
+ "prompt": "A product photo with the logo in the corner",
+ "referenceImages": ["@brand-logo", "@product-style"]
+}
+```
+
+References can be:
+- **Project aliases**: `@logo`, `@brand-style`
+- **Flow aliases**: `@hero` (with flowId context)
+- **Technical aliases**: `@last`, `@first`, `@upload`
+- **Image UUIDs**: `550e8400-e29b-41d4-a716-446655440000`
+
+### Auto-Detection from Prompt
+
+Aliases in the prompt are automatically detected and used as references:
+
+```json
+{
+ "prompt": "Create a banner using @brand-logo with blue background"
+}
+// @brand-logo is auto-detected and added to referenceImages
+```
+
+### Reference Limits
+
+| Constraint | Limit |
+|------------|-------|
+| Max references | 3 images |
+| Max file size | 5MB per image |
+| Supported formats | PNG, JPEG, WebP |
+
+### Response with References
+
+```json
+{
+ "data": {
+ "id": "550e8400-...",
+ "prompt": "Create a banner using @brand-logo",
+ "referencedImages": [
+ { "imageId": "7c4ccf47-...", "alias": "@brand-logo" }
+ ],
+ "referenceImages": [
+ {
+ "id": "7c4ccf47-...",
+ "storageUrl": "http://...",
+ "alias": "@brand-logo"
+ }
+ ]
+ }
+}
+```
+
+---
+
+## Alias Assignment
+
+Assign aliases to generated images for easy referencing.
+
+### Project-Scoped Alias
+
+Use `alias` parameter to assign a project-wide alias:
+
+```json
+{
+ "prompt": "A hero banner image",
+ "alias": "@hero-banner"
+}
+```
+
+The output image will be accessible via `@hero-banner` anywhere in the project.
+
+### Flow-Scoped Alias
+
+Use `flowAlias` parameter to assign a flow-specific alias:
+
+```json
+{
+ "prompt": "A hero image variation",
+ "flowId": "550e8400-...",
+ "flowAlias": "@best"
+}
+```
+
+The alias `@best` is only accessible within this flow's context.
+
+### Alias Format
+
+| Rule | Description |
+|------|-------------|
+| Prefix | Must start with `@` |
+| Characters | Alphanumeric, underscore, hyphen |
+| Pattern | `@[a-zA-Z0-9_-]+` |
+| Max length | 50 characters |
+| Examples | `@logo`, `@hero-bg`, `@image_1` |
+
+### Reserved Aliases
+
+These aliases are computed automatically and cannot be assigned:
+
+| Alias | Description |
+|-------|-------------|
+| `@last` | Most recently generated image in flow |
+| `@first` | First generated image in flow |
+| `@upload` | Most recently uploaded image in flow |
+
+### Override Behavior
+
+When assigning an alias that already exists:
+- The **new image gets the alias**
+- The **old image loses the alias** (alias set to null)
+- The old image is **not deleted**, just unlinked
+
+---
+
+## 3-Tier Alias Resolution
+
+Aliases are resolved in this order of precedence:
+
+### 1. Technical Aliases (Highest Priority)
+
+Computed on-the-fly, require flow context:
+
+```
+GET /api/v1/images/@last?flowId=550e8400-...
+```
+
+| Alias | Returns |
+|-------|---------|
+| `@last` | Last generated image in flow |
+| `@first` | First generated image in flow |
+| `@upload` | Last uploaded image in flow |
+
+### 2. Flow Aliases
+
+Stored in flow's `aliases` JSONB field:
+
+```
+GET /api/v1/images/@hero?flowId=550e8400-...
+```
+
+Different flows can have the same alias pointing to different images.
+
+### 3. Project Aliases (Lowest Priority)
+
+Stored in image's `alias` column:
+
+```
+GET /api/v1/images/@logo
+```
+
+Global across the project, unique per project.
+
+### Resolution Example
+
+```
+// Request with flowId
+GET /api/v1/images/@hero?flowId=abc-123
+
+// Resolution order:
+// 1. Is "@hero" a technical alias? No
+// 2. Does flow abc-123 have "@hero" in aliases? Check flows.aliases JSONB
+// 3. Does any image have alias = "@hero"? Check images.alias column
+```
+
+---
+
+## Flow Integration
+
+Flows organize related generations into chains.
+
+### Lazy Flow Creation
+
+When `flowId` is not provided, a pending flow ID is generated:
+
+```json
+// Request
+{
+ "prompt": "A red car"
+ // No flowId
+}
+
+// Response
+{
+ "data": {
+ "id": "gen-123",
+ "flowId": "flow-456" // Auto-generated, flow record not created yet
+ }
+}
+```
+
+The flow record is created when:
+- A second generation uses the same `flowId`
+- A `flowAlias` is assigned to any generation in the flow
+
+### Eager Flow Creation
+
+When `flowAlias` is provided, the flow is created immediately:
+
+```json
+{
+ "prompt": "A hero banner",
+ "flowAlias": "@hero-flow"
+}
+```
+
+### No Flow Association
+
+To explicitly create without flow association:
+
+```json
+{
+ "prompt": "A standalone image",
+ "flowId": null
+}
+```
+
+### flowId Behavior Summary
+
+| Value | Behavior |
+|-------|----------|
+| `undefined` (not provided) | Auto-generate pendingFlowId, lazy creation |
+| `null` (explicitly null) | No flow association |
+| `"uuid-string"` | Use provided ID, create flow if doesn't exist |
+
+---
+
+## Regeneration
+
+### Regenerate Generation
+
+Recreate an image using the exact same parameters:
+
+```
+POST /api/v1/generations/:id/regenerate
+```
+
+**Behavior:**
+- Uses exact same prompt, aspect ratio, references
+- **Preserves** output image ID and URL
+- Works regardless of current status
+- No request body needed
+
+**Response:** Same as original generation with new image
+
+### Update and Regenerate
+
+Use PUT to modify parameters with smart regeneration:
+
+```
+PUT /api/v1/generations/:id
+```
+
+```json
+{
+ "prompt": "A blue car instead",
+ "aspectRatio": "1:1"
+}
+```
+
+**Smart Behavior:**
+
+| Changed Field | Triggers Regeneration |
+|---------------|----------------------|
+| `prompt` | Yes |
+| `aspectRatio` | Yes |
+| `flowId` | No (metadata only) |
+| `meta` | No (metadata only) |
+
+### Flow Regenerate
+
+Regenerate the most recent generation in a flow:
+
+```
+POST /api/v1/flows/:id/regenerate
+```
+
+**Behavior:**
+- Finds the most recent generation in flow
+- Regenerates with exact same parameters
+- Returns error if flow has no generations
+
+---
+
+## Flow Management
+
+### List Flows
+
+```
+GET /api/v1/flows
+```
+
+**Query Parameters:**
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `limit` | number | `20` | Results per page (max: 100) |
+| `offset` | number | `0` | Pagination offset |
+
+**Response:**
+
+```json
+{
+ "data": [
+ {
+ "id": "flow-456",
+ "projectId": "project-123",
+ "aliases": { "@hero": "img-789", "@best": "img-abc" },
+ "generationCount": 5,
+ "imageCount": 7,
+ "createdAt": "2025-11-28T10:00:00.000Z"
+ }
+ ],
+ "pagination": { "limit": 20, "offset": 0, "total": 3, "hasMore": false }
+}
+```
+
+### Get Flow
+
+```
+GET /api/v1/flows/:id
+```
+
+Returns flow with computed counts and aliases.
+
+### List Flow Generations
+
+```
+GET /api/v1/flows/:id/generations
+```
+
+Returns all generations in the flow, ordered by creation date (newest first).
+
+### List Flow Images
+
+```
+GET /api/v1/flows/:id/images
+```
+
+Returns all images in the flow (generated and uploaded).
+
+### Update Flow Aliases
+
+```
+PUT /api/v1/flows/:id/aliases
+```
+
+```json
+{
+ "aliases": {
+ "@hero": "image-id-123",
+ "@best": "image-id-456"
+ }
+}
+```
+
+**Behavior:** Merges with existing aliases (does not replace all).
+
+### Remove Flow Alias
+
+```
+DELETE /api/v1/flows/:id/aliases/:alias
+```
+
+Example: `DELETE /api/v1/flows/flow-456/aliases/@hero`
+
+### Delete Flow
+
+```
+DELETE /api/v1/flows/:id
+```
+
+**Cascade Behavior:**
+- Flow record is **hard deleted**
+- All generations in flow are **hard deleted**
+- Images **without** project alias: **hard deleted** with MinIO cleanup
+- Images **with** project alias: **kept**, but `flowId` set to null
+
+---
+
+## Full Request Example
+
+```json
+// POST /api/v1/generations
+{
+ "prompt": "A professional product photo using @brand-style and @product-template",
+ "aspectRatio": "1:1",
+ "autoEnhance": true,
+ "enhancementOptions": { "template": "product" },
+ "flowId": "campaign-flow-123",
+ "alias": "@latest-product",
+ "flowAlias": "@hero",
+ "meta": { "campaign": "summer-2025" }
+}
+```
+
+**What happens:**
+1. `@brand-style` and `@product-template` resolved and used as references
+2. Prompt enhanced using "product" template
+3. Generation created in flow `campaign-flow-123`
+4. Output image assigned project alias `@latest-product`
+5. Output image assigned flow alias `@hero` in the flow
+6. Custom metadata stored
+
+---
+
+## Response Fields (Additional)
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `flowId` | string | Associated flow UUID |
+| `alias` | string | Project-scoped alias (on outputImage) |
+| `referencedImages` | array | Resolved references: `[{ imageId, alias }]` |
+| `referenceImages` | array | Full image details of references |
+
+---
+
+## Error Codes
+
+| HTTP Status | Code | Description |
+|-------------|------|-------------|
+| 400 | `ALIAS_FORMAT_CHECK` | Alias must start with @ |
+| 400 | `RESERVED_ALIAS` | Cannot use technical alias |
+| 404 | `ALIAS_NOT_FOUND` | Referenced alias doesn't exist |
+| 404 | `FLOW_NOT_FOUND` | Flow does not exist |
+
+---
+
+## See Also
+
+- [Basic Generation](image-generation.md) - Simple generation
+- [Image Upload](images-upload.md) - Upload with aliases
+- [Live URLs](live-url.md) - CDN and live generation
diff --git a/docs/api/image-generation.md b/docs/api/image-generation.md
index 9f8060a..2c8eb60 100644
--- a/docs/api/image-generation.md
+++ b/docs/api/image-generation.md
@@ -1,805 +1,343 @@
-# Banatie API - Image Generation (v1)
+# Image Generation API
-All endpoints require Project Key authentication via `X-API-Key` header and are rate-limited to 100 requests/hour.
+Basic image generation using AI. For advanced features like references, aliases, and flows, see [image-generation-advanced.md](image-generation-advanced.md).
+
+All endpoints require Project Key authentication via `X-API-Key` header.
---
-## Generations
+## Create Generation
-### POST /api/v1/generations
+```
+POST /api/v1/generations
+```
-Create new image generation with optional reference images, aliases, and auto-enhancement.
+Generate an AI image from a text prompt.
-**Authentication:** Project Key required
+**Request Body:**
-**Parameters:**
-- `prompt` - Text description for image (required)
-- `referenceImages` - Array of image references (aliases or image IDs)
-- `aspectRatio` - Image aspect ratio (e.g., "16:9", "1:1", "3:2", "9:16", default: "1:1")
-- `flowId` - Associate generation with a flow (UUID)
-- `alias` - Assign project-scoped alias to output image (@custom-name)
-- `flowAlias` - Assign flow-scoped alias to output image (requires flowId)
-- `autoEnhance` - Enable prompt enhancement (boolean, default: true)
-- `enhancementOptions` - Enhancement configuration (object, optional)
- - `template` - Enhancement template: "photorealistic", "illustration", "minimalist", "sticker", "product", "comic", "general"
-- `meta` - Custom metadata (JSON object)
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `prompt` | string | Yes | - | Text description of the image to generate |
+| `aspectRatio` | string | No | `"1:1"` | Image aspect ratio |
+| `autoEnhance` | boolean | No | `true` | Enable AI prompt enhancement |
+| `enhancementOptions` | object | No | - | Enhancement configuration |
+| `enhancementOptions.template` | string | No | `"general"` | Enhancement template |
+| `meta` | object | No | `{}` | Custom metadata |
-**Purpose:** Generate AI images with reference support and automatic alias assignment
+**Example Request:**
-**Response:** 201 Created with generation details, status, and output image
+```json
+{
+ "prompt": "a red sports car on a mountain road",
+ "aspectRatio": "16:9",
+ "autoEnhance": true,
+ "enhancementOptions": {
+ "template": "photorealistic"
+ }
+}
+```
+
+**Response:** `201 Created`
+
+```json
+{
+ "success": true,
+ "data": {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
+ "prompt": "A photorealistic establishing shot of a sleek red sports car...",
+ "originalPrompt": "a red sports car on a mountain road",
+ "autoEnhance": true,
+ "aspectRatio": "16:9",
+ "status": "pending",
+ "outputImageId": null,
+ "processingTimeMs": null,
+ "createdAt": "2025-11-28T10:00:00.000Z",
+ "updatedAt": "2025-11-28T10:00:00.000Z"
+ }
+}
+```
---
-### GET /api/v1/generations
+## Aspect Ratios
-List generations with filtering and pagination.
+Supported aspect ratios for image generation:
-**Authentication:** Project Key required
+| Aspect Ratio | Use Case |
+|--------------|----------|
+| `1:1` | Square images, social media posts, profile pictures |
+| `16:9` | Landscape, hero banners, video thumbnails |
+| `9:16` | Portrait, mobile screens, stories |
+| `3:2` | Photography standard, print |
+| `21:9` | Ultra-wide banners, cinematic |
+
+---
+
+## Prompt Enhancement
+
+By default, prompts are automatically enhanced by AI to produce better results.
+
+### How It Works
+
+When `autoEnhance: true` (default):
+- Your original prompt is preserved in `originalPrompt`
+- AI enhances it with style details, lighting, composition
+- The enhanced version is stored in `prompt` and used for generation
+
+When `autoEnhance: false`:
+- Both `prompt` and `originalPrompt` contain your original text
+- No AI enhancement is applied
+
+### Enhancement Templates
+
+Use `enhancementOptions.template` to guide the enhancement style:
+
+| Template | Description | Best For |
+|----------|-------------|----------|
+| `general` | Balanced enhancement (default) | Most use cases |
+| `photorealistic` | Photography terms, lighting, camera details | Realistic photos |
+| `illustration` | Art style, composition, color palette | Artwork, drawings |
+| `minimalist` | Clean, simple, essential elements | Logos, icons |
+| `sticker` | Bold outlines, limited colors, vector style | Stickers, emojis |
+| `product` | Studio lighting, materials, lifestyle context | E-commerce |
+| `comic` | Action lines, expressions, panel composition | Comics, manga |
+
+### Example: With Enhancement
+
+```json
+// Request
+{
+ "prompt": "a cat",
+ "autoEnhance": true,
+ "enhancementOptions": { "template": "photorealistic" }
+}
+
+// Response
+{
+ "prompt": "A photorealistic close-up portrait of a domestic cat with soft fur, captured with an 85mm lens at f/1.8, natural window lighting creating soft shadows, detailed whiskers and expressive eyes, shallow depth of field with creamy bokeh background",
+ "originalPrompt": "a cat",
+ "autoEnhance": true
+}
+```
+
+### Example: Without Enhancement
+
+```json
+// Request
+{
+ "prompt": "a cat sitting on a windowsill",
+ "autoEnhance": false
+}
+
+// Response
+{
+ "prompt": "a cat sitting on a windowsill",
+ "originalPrompt": "a cat sitting on a windowsill",
+ "autoEnhance": false
+}
+```
+
+---
+
+## Generation Status
+
+Generations go through these status stages:
+
+| Status | Description |
+|--------|-------------|
+| `pending` | Generation created, waiting to start |
+| `processing` | AI is generating the image |
+| `success` | Image generated successfully |
+| `failed` | Generation failed (see `errorMessage`) |
+
+Poll the generation endpoint to check status:
+
+```
+GET /api/v1/generations/:id
+```
+
+When `status: "success"`, the `outputImageId` field contains the generated image ID.
+
+---
+
+## List Generations
+
+```
+GET /api/v1/generations
+```
+
+List all generations with optional filters.
**Query Parameters:**
-- `flowId` - Filter by flow (UUID)
-- `status` - Filter by status (pending|processing|success|failed)
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
-- `includeDeleted` - Include soft-deleted records (boolean, default: false)
-**Purpose:** Browse generation history with optional filters
-
----
-
-### GET /api/v1/generations/:id
-
-Get single generation with full details.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Generation UUID (path parameter)
-
-**Purpose:** View complete generation details including output image, reference images, flow association, and timestamps
-
----
-
-### PUT /api/v1/generations/:id
-
-Update generation parameters with automatic regeneration.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Generation UUID (path parameter)
-- `prompt` - New prompt (triggers regeneration)
-- `aspectRatio` - New aspect ratio (triggers regeneration)
-- `flowId` - Change flow association (null to detach, no regeneration)
-- `meta` - Update custom metadata (no regeneration)
-
-**Purpose:** Update generation settings with intelligent regeneration behavior
-
-**Notes:**
-- Changing `prompt` or `aspectRatio` triggers automatic regeneration
-- Changing `flowId` or `meta` updates metadata only (no regeneration)
-- Regeneration replaces existing output image (preserves ID and URLs)
-
----
-
-### POST /api/v1/generations/:id/regenerate
-
-Regenerate existing generation with exact same parameters.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Generation UUID (path parameter)
-
-**Purpose:** Recreate image using original parameters (prompt, aspect ratio, references)
-
-**Notes:**
-- Uses exact same parameters as original generation
-- Works regardless of current status (success, failed, pending)
-- Replaces existing output image (preserves ID and URLs)
-- No parameter modifications allowed (use PUT for changes)
-- Useful for refreshing stale images or recovering from failures
-
----
-
-### POST /api/v1/generations/:id/retry
-
-**DEPRECATED:** Use POST /api/v1/generations/:id/regenerate instead
-
-Legacy endpoint maintained for backward compatibility. Delegates to /regenerate endpoint.
-
----
-
-### DELETE /api/v1/generations/:id
-
-Delete generation and its output image.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Generation UUID (path parameter)
-
-**Purpose:** Remove generation record and associated output image
-
-**Notes:**
-- Soft delete generation record (sets deletedAt timestamp)
-- Hard delete output image if no project/flow aliases exist
-- Soft delete output image if aliases exist (preserves for CDN access)
-- Cascades to remove generation-image relationships
-
----
-
-## Flows
-
-Flows organize related generations into chains and support flow-scoped aliases. Flows are created automatically when generations or uploads specify a flowId.
-
-### GET /api/v1/flows
-
-List all flows with pagination.
-
-**Authentication:** Project Key required
-
-**Query Parameters:**
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
-
-**Purpose:** Browse all flows with computed generation and image counts
-
-**Notes:**
-- Flows are created automatically when:
- - A generation/upload specifies a flowId
- - A generation/upload provides a flowAlias (eager creation)
-
----
-
-### GET /api/v1/flows/:id
-
-Get single flow with details.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-
-**Purpose:** View flow metadata, aliases, and computed counts (generationCount, imageCount)
-
----
-
-### GET /api/v1/flows/:id/generations
-
-List all generations in a flow.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-
-**Query Parameters:**
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
-
-**Purpose:** View all generations associated with this flow, ordered by creation date (newest first)
-
----
-
-### GET /api/v1/flows/:id/images
-
-List all images in a flow.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-
-**Query Parameters:**
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
-
-**Purpose:** View all images (generated and uploaded) associated with this flow, ordered by creation date (newest first)
-
----
-
-### PUT /api/v1/flows/:id/aliases
-
-Update flow aliases.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-- `aliases` - Object mapping alias names to image IDs (request body)
-
-**Purpose:** Add or update flow-scoped aliases for image referencing
-
-**Notes:**
-- Merges with existing aliases (does not replace all)
-- Aliases must map to valid image IDs
-- Aliases are stored in JSONB field for efficient lookups
-
----
-
-### DELETE /api/v1/flows/:id/aliases/:alias
-
-Remove specific flow alias.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-- `alias` - Alias name to remove (path parameter, e.g., "@hero")
-
-**Purpose:** Delete a single alias from flow's alias map, other aliases remain unchanged
-
----
-
-### POST /api/v1/flows/:id/regenerate
-
-Regenerate the most recent generation in a flow.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-
-**Purpose:** Regenerate the latest generation in the flow
-
-**Notes:**
-- Identifies the most recent generation (ordered by creation date)
-- Uses exact same parameters from original generation
-- Returns error if flow has no generations
-- Replaces existing output image (preserves ID and URLs)
-
----
-
-### DELETE /api/v1/flows/:id
-
-Delete flow.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Flow UUID (path parameter)
-
-**Purpose:** Remove flow (hard delete)
-
-**Notes:**
-- Flow record is hard deleted (no soft delete)
-- Generations remain intact (not cascaded)
-- Images remain intact (not cascaded)
-- Flow-scoped aliases are removed with flow
-- Generations and images lose their flow association but remain accessible
-
----
-
-## Images
-
-Database-tracked image management with upload, listing, metadata updates, and deletion.
-
-### POST /api/v1/images/upload
-
-Upload image file with database tracking.
-
-**Authentication:** Project Key required
-
-**Form Parameters (multipart/form-data):**
-- `file` - Image file (required, max 5MB, PNG/JPEG/WebP)
-- `alias` - Project-scoped alias (optional, e.g., "@logo")
-- `flowId` - Associate with flow (UUID, optional)
-- `flowAlias` - Flow-scoped alias (optional, requires flowId, triggers eager creation)
-- `meta` - Custom metadata (JSON string, optional)
-
-**Purpose:** Upload image with automatic database record creation and storage
-
-**FlowId Behavior:**
-- `undefined` (not provided) → generates new UUID for automatic flow creation
-- `null` (explicitly null) → no flow association
-- `string` (specific value) → uses provided flow ID
-
-**Notes:**
-- Creates both MinIO storage file and database entry
-- Supports PNG, JPEG, JPG, WebP formats
-- Maximum file size: 5MB
-- Eager flow creation when flowAlias is provided
-
----
-
-### GET /api/v1/images
-
-List images with filtering and pagination.
-
-**Authentication:** Project Key required
-
-**Query Parameters:**
-- `flowId` - Filter by flow (UUID)
-- `source` - Filter by source (generated|uploaded)
-- `alias` - Filter by project alias (exact match)
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
-- `includeDeleted` - Include soft-deleted records (boolean, default: false)
-
-**Purpose:** Browse image library with optional filters
-
----
-
-### GET /api/v1/images/resolve/:alias
-
-Resolve alias to image using 3-tier precedence.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `alias` - Alias to resolve (path parameter, e.g., "@hero", "@last")
-
-**Query Parameters:**
-- `flowId` - Flow context for resolution (UUID, optional)
-
-**Purpose:** Lookup image by alias with technical → flow → project precedence
-
-**Resolution Order:**
-1. Technical aliases (if matches @last, @first, @upload) - computed on-the-fly
-2. Flow aliases (if flowId provided) - looked up in flow's JSONB aliases field
-3. Project aliases (global) - looked up in images.alias column
-
-**Returns:** Image record with resolution metadata (imageId, scope, flowId, image details)
-
----
-
-### GET /api/v1/images/:id
-
-Get single image by ID.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Image UUID (path parameter)
-
-**Purpose:** View complete image metadata including storage URLs, project/flow associations, alias, source, file metadata, focal point, and custom metadata
-
----
-
-### PUT /api/v1/images/:id
-
-Update image metadata.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Image UUID (path parameter)
-- `focalPoint` - Update focal point coordinates (object: {x: 0.0-1.0, y: 0.0-1.0}, optional)
-- `meta` - Update custom metadata (JSON object, optional)
-
-**Purpose:** Modify non-generative image properties
-
-**Notes:**
-- Alias assignment moved to separate endpoint PUT /images/:id/alias
-- Focal point used for image cropping
-
----
-
-### PUT /api/v1/images/:id/alias
-
-Assign or update project-scoped alias.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Image UUID (path parameter)
-- `alias` - Alias name (string, required in request body, e.g., "@hero-bg")
-
-**Purpose:** Set project-level alias for image referencing
-
-**Notes:**
-- Alias must start with @ symbol
-- Must be unique within the project
-- Validates alias format and reserved names
-- Checks for conflicts with existing aliases
-- Replaces existing alias if image already has one
-
----
-
-### DELETE /api/v1/images/:id
-
-Delete image with storage cleanup.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `id` - Image UUID (path parameter)
-
-**Purpose:** Permanently remove image and its storage file
-
-**Notes:**
-- Hard delete of image record (no soft delete)
-- Removes file from MinIO storage permanently
-- Cascades to delete generation-image relationships
-- Removes image from flow aliases (if present)
-- Cannot be undone - use with caution
-
----
-
-## CDN Endpoints
-
-Public endpoints for serving images and live URL generation without authentication.
-
-### GET /cdn/:orgSlug/:projectSlug/img/:filenameOrAlias
-
-Serve images by filename or project-scoped alias via public CDN.
-
-**Authentication:** None - Public endpoint
-
-**Parameters:**
-- `orgSlug` - Organization slug (path parameter)
-- `projectSlug` - Project slug (path parameter)
-- `filenameOrAlias` - Filename or @alias (path parameter)
-
-**Purpose:** Public image serving for websites and applications
-
-**Response Format:** Raw image bytes (not JSON)
-
-**Response Headers:**
-- `Content-Type` - image/jpeg (or appropriate MIME type)
-- `Content-Length` - Actual byte length
-- `Cache-Control` - public, max-age=31536000 (1 year)
-- `X-Image-Id` - Image database UUID
-
-**Access Methods:**
-- Filename: `GET /cdn/acme/website/img/hero-background.jpg`
-- Alias: `GET /cdn/acme/website/img/@hero`
-
----
-
-### GET /cdn/:orgSlug/:projectSlug/live/:scope
-
-Live URL generation with automatic caching and scope management.
-
-**Authentication:** None - Public endpoint
-
-**Rate Limit:** 10 new generations per hour per IP (cache hits excluded)
-
-**Parameters:**
-- `orgSlug` - Organization slug (path parameter)
-- `projectSlug` - Project slug (path parameter)
-- `scope` - Scope identifier (path parameter, alphanumeric + hyphens + underscores)
-
-**Query Parameters:**
-- `prompt` - Image description (required)
-- `aspectRatio` - Image aspect ratio (optional, default: "1:1")
-- `autoEnhance` - Enable prompt enhancement (boolean, optional)
-- `template` - Enhancement template (optional): photorealistic, illustration, minimalist, sticker, product, comic, general
-
-**Purpose:** On-demand image generation via URL parameters for dynamic content
-
-**Response Format:** Raw image bytes (not JSON)
-
-**Response Headers (Cache HIT):**
-- `Content-Type` - image/jpeg
-- `Content-Length` - Actual byte length
-- `Cache-Control` - public, max-age=31536000
-- `X-Cache-Status` - HIT
-- `X-Scope` - Scope identifier
-- `X-Image-Id` - Image UUID
-
-**Response Headers (Cache MISS):**
-- `Content-Type` - image/jpeg
-- `Content-Length` - Actual byte length
-- `Cache-Control` - public, max-age=31536000
-- `X-Cache-Status` - MISS
-- `X-Scope` - Scope identifier
-- `X-Generation-Id` - Generation UUID
-- `X-Image-Id` - Image UUID
-- `X-RateLimit-Limit` - 10
-- `X-RateLimit-Remaining` - Remaining requests
-- `X-RateLimit-Reset` - Seconds until reset
-
-**Caching Behavior:**
-- **Cache HIT:** Returns existing image instantly, no rate limit check
-- **Cache MISS:** Generates new image, counts toward IP rate limit
-- Cache key computed from: prompt + aspectRatio + autoEnhance + template
-- Cached images stored with meta.isLiveUrl = true
-
-**Scope Management:**
-- Scopes separate generation budgets (e.g., "hero-section", "gallery")
-- Auto-created on first use if allowNewLiveScopes = true
-- Generation limits per scope (default: 30)
-- Scope stats tracked (currentGenerations, lastGeneratedAt)
-
-**IP Rate Limiting:**
-- 10 new generations per hour per IP address
-- Separate from API key rate limits
-- Cache hits do NOT count toward limit
-- Only new generations (cache MISS) count
-- Supports X-Forwarded-For header for proxy setups
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `status` | string | - | Filter by status: `pending`, `processing`, `success`, `failed` |
+| `limit` | number | `20` | Results per page (max: 100) |
+| `offset` | number | `0` | Pagination offset |
+| `includeDeleted` | boolean | `false` | Include soft-deleted records |
**Example:**
+
```
-GET /cdn/acme/website/live/hero-section?prompt=mountain+landscape&aspectRatio=16:9
+GET /api/v1/generations?status=success&limit=10
+```
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "data": [
+ {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "prompt": "A photorealistic establishing shot...",
+ "originalPrompt": "a red sports car",
+ "autoEnhance": true,
+ "aspectRatio": "16:9",
+ "status": "success",
+ "outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
+ "processingTimeMs": 8500,
+ "createdAt": "2025-11-28T10:00:00.000Z"
+ }
+ ],
+ "pagination": {
+ "limit": 10,
+ "offset": 0,
+ "total": 42,
+ "hasMore": true
+ }
+}
```
---
-## Live Scopes Management
+## Get Generation
-Authenticated endpoints for managing live URL scopes. All require Project Key authentication.
+```
+GET /api/v1/generations/:id
+```
-### POST /api/v1/live/scopes
+Get a single generation with full details.
-Create new live scope manually with settings.
+**Response:**
-**Authentication:** Project Key required
-
-**Parameters:**
-- `slug` - Unique scope identifier (required, alphanumeric + hyphens + underscores)
-- `allowNewGenerations` - Allow new generations in scope (boolean, default: true)
-- `newGenerationsLimit` - Maximum generations allowed (number, default: 30)
-- `meta` - Custom metadata (JSON object, optional)
-
-**Purpose:** Pre-configure scope with specific settings before first use
-
-**Notes:**
-- Scopes are typically auto-created via live URLs
-- Slug must be unique within the project
-- Slug format: alphanumeric + hyphens + underscores only
+```json
+{
+ "success": true,
+ "data": {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
+ "prompt": "A photorealistic establishing shot of a sleek red sports car...",
+ "originalPrompt": "a red sports car on a mountain road",
+ "autoEnhance": true,
+ "aspectRatio": "16:9",
+ "status": "success",
+ "outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
+ "outputImage": {
+ "id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
+ "storageUrl": "http://localhost:9000/banatie/default/project-id/generated/image.png",
+ "mimeType": "image/png",
+ "width": 1792,
+ "height": 1024,
+ "fileSize": 1909246
+ },
+ "processingTimeMs": 8500,
+ "retryCount": 0,
+ "errorMessage": null,
+ "meta": {},
+ "createdAt": "2025-11-28T10:00:00.000Z",
+ "updatedAt": "2025-11-28T10:00:08.500Z"
+ }
+}
+```
---
-### GET /api/v1/live/scopes
+## Delete Generation
-List all live scopes with pagination and statistics.
+```
+DELETE /api/v1/generations/:id
+```
-**Authentication:** Project Key required
+Delete a generation and its output image.
-**Query Parameters:**
-- `slug` - Filter by exact slug match (optional)
-- `limit` - Results per page (default: 20, max: 100)
-- `offset` - Pagination offset (default: 0)
+**Response:** `200 OK`
-**Purpose:** Browse all scopes (both auto-created and manually created)
-
-**Returns:** Scopes with currentGenerations count, lastGeneratedAt, settings, and metadata
-
----
-
-### GET /api/v1/live/scopes/:slug
-
-Get single live scope by slug with complete statistics.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `slug` - Scope slug identifier (path parameter)
-
-**Purpose:** View detailed scope information including generation count, last generation timestamp, settings, and metadata
-
----
-
-### PUT /api/v1/live/scopes/:slug
-
-Update live scope settings and metadata.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `slug` - Scope slug identifier (path parameter)
-- `allowNewGenerations` - Allow/disallow new generations (boolean, optional)
-- `newGenerationsLimit` - Update generation limit (number, optional)
-- `meta` - Update custom metadata (JSON object, optional)
-
-**Purpose:** Modify scope configuration
-
-**Notes:**
-- Changes take effect immediately for new live URL requests
-
----
-
-### POST /api/v1/live/scopes/:slug/regenerate
-
-Regenerate images in a live scope.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `slug` - Scope slug identifier (path parameter)
-- `imageId` - Specific image to regenerate (UUID, optional in request body)
-
-**Purpose:** Regenerate either a specific image or all images in the scope
+```json
+{
+ "success": true,
+ "message": "Generation deleted"
+}
+```
**Behavior:**
-- If `imageId` provided: Regenerates specific image only
-- If `imageId` omitted: Regenerates all images in scope
-- Uses exact same parameters (prompt, aspect ratio, etc.)
-- Updates existing images (preserves IDs and URLs)
-- Verifies image belongs to scope before regenerating
-
-**Notes:**
-- Useful for refreshing stale cached images or recovering from failures
+- Generation record is hard deleted
+- Output image is hard deleted (unless it has a project alias)
---
-### DELETE /api/v1/live/scopes/:slug
-
-Delete live scope with cascading image deletion.
-
-**Authentication:** Project Key required
-
-**Parameters:**
-- `slug` - Scope slug identifier (path parameter)
-
-**Purpose:** Permanently remove scope and all cached live URL images
-
-**Notes:**
-- Hard deletes all images in scope (MinIO + database)
-- Follows alias protection rules for each image
-- Hard deletes scope record (no soft delete)
-- Cannot be undone - use with caution
-
----
-
-## Alias System
-
-The v1 API supports a 3-tier alias resolution system for referencing images.
-
-**Alias Format Requirements:**
-- All aliases MUST start with `@` symbol
-- Pattern: `@[a-zA-Z0-9_-]+` (alphanumeric, underscore, hyphen)
-- Maximum length: 50 characters
-- Examples: `@hero`, `@product-main`, `@image_1`
-
-**Technical Aliases** (Computed, Reserved)
-- `@last` - Most recently generated image in project
-- `@first` - First generated image in project
-- `@upload` - Most recently uploaded image in project
-
-**Reserved Aliases** (Cannot be used)
-- Technical aliases: `@last`, `@first`, `@upload`
-- Future reserved: `@all`, `@latest`, `@oldest`, `@random`, `@next`, `@prev`, `@previous`
-
-**Flow Aliases** (Flow-scoped)
-- Stored in flow's aliases JSONB field
-- Only accessible within flow context
-- Set via `PUT /api/v1/flows/:id/aliases`
-- Example: `@hero` in flow A is different from `@hero` in flow B
-
-**Project Aliases** (Global)
-- Unique within project
-- Accessible across all flows
-- Set via `PUT /api/v1/images/:id/alias` or generation/upload parameters
-- Example: `@logo` is the same image across entire project
-
-**Resolution Order:**
-1. Technical aliases (if matches @last, @first, @upload)
-2. Flow aliases (if flowId provided in context)
-3. Project aliases (global lookup)
-
----
-
-## Response Formats
+## Response Fields
### Generation Response
-Generation responses include fields for prompt enhancement tracking:
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Generation UUID |
+| `projectId` | string | Project UUID |
+| `prompt` | string | Prompt used for generation (enhanced if applicable) |
+| `originalPrompt` | string | Original user input |
+| `autoEnhance` | boolean | Whether enhancement was applied |
+| `aspectRatio` | string | Image aspect ratio |
+| `status` | string | Generation status |
+| `outputImageId` | string | Output image UUID (when successful) |
+| `outputImage` | object | Output image details (when successful) |
+| `processingTimeMs` | number | Generation time in milliseconds |
+| `retryCount` | number | Number of retry attempts |
+| `errorMessage` | string | Error details (when failed) |
+| `meta` | object | Custom metadata |
+| `createdAt` | string | ISO timestamp |
+| `updatedAt` | string | ISO timestamp |
-```json
-{
- "id": "550e8400-e29b-41d4-a716-446655440000",
- "projectId": "57c7f7f4d-47de-4d70-9ebd-3807a0b63746",
- "prompt": "A photorealistic establishing shot of a luxurious motor yacht...",
- "originalPrompt": "шикарная моторная яхта движется по живописному озеру...",
- "autoEnhance": true,
- "aspectRatio": "16:9",
- "status": "success",
- "outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
- "outputImage": { ... },
- "referenceImages": [],
- "flowId": null,
- "processingTimeMs": 8980,
- "cost": null,
- "retryCount": 0,
- "errorMessage": null,
- "meta": {},
- "createdAt": "2025-11-23T13:47:00.127Z",
- "updatedAt": "2025-11-23T13:47:09.107Z"
-}
-```
+### Output Image
-**Prompt Enhancement Fields:**
-
-- `prompt` - The actual prompt used for generation (enhanced if `autoEnhance: true`, original if `autoEnhance: false`)
-- `originalPrompt` - The user's original input prompt (always populated for transparency)
-- `autoEnhance` - Boolean indicating if prompt enhancement was applied
-
-**Semantics:**
-- When `autoEnhance: true`:
- - `originalPrompt` = user's original input
- - `prompt` = AI-enhanced version used for generation
- - Example: Original "a cat" → Enhanced "A photorealistic close-up portrait of a cat..."
-
-- When `autoEnhance: false`:
- - `originalPrompt` = user's original input
- - `prompt` = same as originalPrompt (no enhancement)
- - Both fields contain identical values
-
-**Default Behavior:** `autoEnhance` defaults to `true` if not explicitly set to `false`.
-
----
-
-### Image Response
-
-Image responses include actual dimensions and storage access:
-
-```json
-{
- "id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
- "projectId": "57c7f7f4d-47de-4d70-9ebd-3807a0b63746",
- "flowId": null,
- "storageKey": "default/57c7f7f4d-47de-4d70-9ebd-3807a0b63746/generated/2025-11/gen_fd14839b.png",
- "storageUrl": "http://localhost:9000/banatie/default/57c7f7f4d-47de-4d70-9ebd-3807a0b63746/generated/gen_fd14839b.png",
- "mimeType": "image/jpeg",
- "fileSize": 1909246,
- "width": 1792,
- "height": 1024,
- "source": "generated",
- "alias": null,
- "focalPoint": null,
- "fileHash": null,
- "generationId": "fd14839b-d42e-4be9-93b3-e2fb67f7af0d",
- "apiKeyId": "988cb71e-0021-4217-a536-734b097a87b3",
- "meta": {},
- "createdAt": "2025-11-23T13:47:00.127Z",
- "updatedAt": "2025-11-23T13:47:00.127Z",
- "deletedAt": null
-}
-```
-
-**Key Fields:**
-
-- `width` / `height` - Actual image dimensions in pixels (automatically extracted from generated images)
-- `storageUrl` - Direct URL to access the image file (use this for image display)
-- `storageKey` - Internal MinIO storage path
-- `source` - Image origin: "generated" (AI-generated) or "uploaded" (user upload)
-- `alias` - Project-scoped alias (e.g., "@logo"), null if not assigned
-- `focalPoint` - Cropping focal point {x: 0.0-1.0, y: 0.0-1.0}, null if not set
-
-**Image Access:**
-
-To display an image, use the `storageUrl` field which provides direct access to the image file in MinIO storage. For public CDN access, use the `/cdn/:orgSlug/:projectSlug/img/:alias` endpoint.
-
-**Note:** Previous versions included a `url` field that pointed to a non-existent `/download` endpoint. This field has been removed. Always use `storageUrl` for direct image access.
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Image UUID |
+| `storageUrl` | string | Direct URL to image file |
+| `mimeType` | string | Image MIME type |
+| `width` | number | Image width in pixels |
+| `height` | number | Image height in pixels |
+| `fileSize` | number | File size in bytes |
---
## Error Codes
-| Code | Description |
-|------|-------------|
-| 400 | Bad Request - Invalid parameters or validation failure |
-| 401 | Unauthorized - Missing, invalid, expired, or revoked API key |
-| 403 | Forbidden - Scope creation disabled or insufficient permissions |
-| 404 | Not Found - Resource does not exist |
-| 409 | Conflict - Alias or slug already exists |
-| 429 | Too Many Requests - Rate limit or scope generation limit exceeded |
-| 500 | Internal Server Error - Processing or generation failure |
+| HTTP Status | Code | Description |
+|-------------|------|-------------|
+| 400 | `VALIDATION_ERROR` | Invalid parameters |
+| 401 | `UNAUTHORIZED` | Missing or invalid API key |
+| 404 | `GENERATION_NOT_FOUND` | Generation does not exist |
+| 429 | `RATE_LIMIT_EXCEEDED` | Too many requests |
+| 500 | `GENERATION_FAILED` | AI generation failed |
-**Common Error Codes:**
-- `VALIDATION_ERROR` - Invalid parameters or missing required fields
-- `GENERATION_NOT_FOUND` - Generation does not exist
-- `FLOW_NOT_FOUND` - Flow does not exist
-- `IMAGE_NOT_FOUND` - Image or alias not found
-- `ALIAS_CONFLICT` - Alias already exists in project
-- `ALIAS_NOT_FOUND` - Alias does not exist in any scope
-- `SCOPE_INVALID_FORMAT` - Invalid scope slug format
-- `SCOPE_ALREADY_EXISTS` - Scope slug already in use
-- `SCOPE_NOT_FOUND` - Scope does not exist
-- `SCOPE_CREATION_DISABLED` - New scope creation not allowed
-- `SCOPE_GENERATION_LIMIT_EXCEEDED` - Scope generation limit reached
-- `IP_RATE_LIMIT_EXCEEDED` - IP rate limit exceeded for live URLs
-- `ORG_NOT_FOUND` - Organization not found
-- `PROJECT_NOT_FOUND` - Project not found
-- `IMAGE_NOT_IN_SCOPE` - Image doesn't belong to specified scope
+---
-**Common Error Messages:**
-- `"Prompt is required"` - Missing prompt parameter
-- `"Alias already exists"` - Alias conflict in project
-- `"Invalid aspect ratio"` - Unsupported aspect ratio format
-- `"File too large"` - Upload exceeds 5MB limit
-- `"alias_format_check"` - Alias must start with @ symbol (e.g., @hero not hero)
-- `"Rate limit exceeded. Try again in N seconds"` - IP rate limit for live URLs
+## Rate Limits
+
+- **100 requests per hour** per API key
+- Rate limit headers included in response:
+ - `X-RateLimit-Limit`: Maximum requests
+ - `X-RateLimit-Remaining`: Remaining requests
+ - `X-RateLimit-Reset`: Seconds until reset
+
+---
+
+## See Also
+
+- [Advanced Generation](image-generation-advanced.md) - References, aliases, flows
+- [Image Upload](images-upload.md) - Upload and manage images
+- [Live URLs](live-url.md) - CDN and live generation
diff --git a/docs/api/images-upload.md b/docs/api/images-upload.md
new file mode 100644
index 0000000..26c6d53
--- /dev/null
+++ b/docs/api/images-upload.md
@@ -0,0 +1,374 @@
+# Image Upload & Management API
+
+Upload images and manage your image library. For generation, see [image-generation.md](image-generation.md).
+
+All endpoints require Project Key authentication via `X-API-Key` header.
+
+---
+
+## Upload Image
+
+```
+POST /api/v1/images/upload
+```
+
+Upload an image file with optional alias and flow association.
+
+**Content-Type:** `multipart/form-data`
+
+**Form Parameters:**
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `file` | file | Yes | Image file (PNG, JPEG, WebP) |
+| `alias` | string | No | Project-scoped alias (e.g., `@logo`) |
+| `flowId` | string | No | Flow UUID to associate with |
+| `flowAlias` | string | No | Flow-scoped alias (requires flowId) |
+| `meta` | string | No | JSON string with custom metadata |
+
+**File Constraints:**
+
+| Constraint | Limit |
+|------------|-------|
+| Max file size | 5MB |
+| Supported formats | PNG, JPEG, JPG, WebP |
+| MIME types | `image/png`, `image/jpeg`, `image/webp` |
+
+**Example Request (curl):**
+
+```bash
+curl -X POST http://localhost:3000/api/v1/images/upload \
+ -H "X-API-Key: YOUR_PROJECT_KEY" \
+ -F "file=@logo.png" \
+ -F "alias=@brand-logo" \
+ -F 'meta={"tags": ["logo", "brand"]}'
+```
+
+**Response:** `201 Created`
+
+```json
+{
+ "success": true,
+ "data": {
+ "id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
+ "projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
+ "flowId": null,
+ "storageKey": "default/project-id/uploads/2025-11/logo.png",
+ "storageUrl": "http://localhost:9000/banatie/default/project-id/uploads/logo.png",
+ "mimeType": "image/png",
+ "fileSize": 45678,
+ "width": 512,
+ "height": 512,
+ "source": "uploaded",
+ "alias": "@brand-logo",
+ "focalPoint": null,
+ "meta": { "tags": ["logo", "brand"] },
+ "createdAt": "2025-11-28T10:00:00.000Z"
+ }
+}
+```
+
+### flowId Behavior
+
+| Value | Behavior |
+|-------|----------|
+| Not provided | Auto-generate `pendingFlowId`, lazy flow creation |
+| `null` | No flow association |
+| `"uuid"` | Associate with specified flow |
+
+### Upload with Flow
+
+```bash
+# Associate with existing flow
+curl -X POST .../images/upload \
+ -F "file=@reference.png" \
+ -F "flowId=flow-123" \
+ -F "flowAlias=@reference"
+```
+
+---
+
+## List Images
+
+```
+GET /api/v1/images
+```
+
+List all images with filtering and pagination.
+
+**Query Parameters:**
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `flowId` | string | - | Filter by flow UUID |
+| `source` | string | - | Filter by source: `generated`, `uploaded` |
+| `alias` | string | - | Filter by exact alias match |
+| `limit` | number | `20` | Results per page (max: 100) |
+| `offset` | number | `0` | Pagination offset |
+| `includeDeleted` | boolean | `false` | Include soft-deleted records |
+
+**Example:**
+
+```
+GET /api/v1/images?source=uploaded&limit=10
+```
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "data": [
+ {
+ "id": "7c4ccf47-...",
+ "storageUrl": "http://...",
+ "source": "uploaded",
+ "alias": "@brand-logo",
+ "width": 512,
+ "height": 512,
+ "createdAt": "2025-11-28T10:00:00.000Z"
+ }
+ ],
+ "pagination": {
+ "limit": 10,
+ "offset": 0,
+ "total": 25,
+ "hasMore": true
+ }
+}
+```
+
+---
+
+## Get Image
+
+```
+GET /api/v1/images/:id_or_alias
+```
+
+Get a single image by UUID or alias.
+
+**Path Parameter:**
+- `id_or_alias` - Image UUID or `@alias`
+
+**Query Parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `flowId` | string | Flow context for alias resolution |
+
+**Examples:**
+
+```
+# By UUID
+GET /api/v1/images/7c4ccf47-41ce-4718-afbc-8c553b2c631a
+
+# By project alias
+GET /api/v1/images/@brand-logo
+
+# By technical alias (requires flowId)
+GET /api/v1/images/@last?flowId=flow-123
+
+# By flow alias
+GET /api/v1/images/@hero?flowId=flow-123
+```
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "data": {
+ "id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
+ "projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
+ "flowId": null,
+ "storageKey": "default/project-id/uploads/2025-11/logo.png",
+ "storageUrl": "http://localhost:9000/banatie/.../logo.png",
+ "mimeType": "image/png",
+ "fileSize": 45678,
+ "width": 512,
+ "height": 512,
+ "source": "uploaded",
+ "alias": "@brand-logo",
+ "focalPoint": null,
+ "fileHash": null,
+ "generationId": null,
+ "meta": { "tags": ["logo", "brand"] },
+ "createdAt": "2025-11-28T10:00:00.000Z",
+ "updatedAt": "2025-11-28T10:00:00.000Z",
+ "deletedAt": null
+ }
+}
+```
+
+---
+
+## Update Image Metadata
+
+```
+PUT /api/v1/images/:id_or_alias
+```
+
+Update image metadata (focal point, custom metadata).
+
+**Request Body:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `focalPoint` | object | Focal point: `{ x: 0.0-1.0, y: 0.0-1.0 }` |
+| `meta` | object | Custom metadata |
+
+**Example:**
+
+```json
+// PUT /api/v1/images/@brand-logo
+{
+ "focalPoint": { "x": 0.5, "y": 0.3 },
+ "meta": {
+ "description": "Updated brand logo",
+ "tags": ["logo", "brand", "2025"]
+ }
+}
+```
+
+**Response:** Updated image object.
+
+> **Note:** Alias assignment has its own dedicated endpoint.
+
+---
+
+## Assign Alias
+
+```
+PUT /api/v1/images/:id_or_alias/alias
+```
+
+Assign or remove a project-scoped alias.
+
+**Request Body:**
+
+```json
+// Assign alias
+{ "alias": "@new-logo" }
+
+// Remove alias
+{ "alias": null }
+```
+
+**Override Behavior:**
+- If another image has this alias, it loses the alias
+- The new image gets the alias
+- Old image is preserved, just unlinked
+
+**Example:**
+
+```bash
+curl -X PUT http://localhost:3000/api/v1/images/7c4ccf47-.../alias \
+ -H "X-API-Key: YOUR_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"alias": "@primary-logo"}'
+```
+
+---
+
+## Delete Image
+
+```
+DELETE /api/v1/images/:id_or_alias
+```
+
+Permanently delete an image and its storage file.
+
+**Behavior:**
+- **Hard delete** - image record permanently removed
+- Storage file deleted from MinIO
+- Cascading updates:
+ - Related generations: `outputImageId` set to null
+ - Flow aliases: image removed from flow's aliases
+ - Referenced images: removed from generation's referencedImages
+
+**Response:** `200 OK`
+
+```json
+{
+ "success": true,
+ "message": "Image deleted"
+}
+```
+
+> **Warning:** This cannot be undone. The image file is permanently removed.
+
+---
+
+## Image Response Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Image UUID |
+| `projectId` | string | Project UUID |
+| `flowId` | string | Associated flow UUID (null if none) |
+| `storageKey` | string | Internal storage path |
+| `storageUrl` | string | **Direct URL to access image** |
+| `mimeType` | string | Image MIME type |
+| `fileSize` | number | File size in bytes |
+| `width` | number | Image width in pixels |
+| `height` | number | Image height in pixels |
+| `source` | string | `"generated"` or `"uploaded"` |
+| `alias` | string | Project-scoped alias (null if none) |
+| `focalPoint` | object | `{ x, y }` coordinates (0.0-1.0) |
+| `fileHash` | string | SHA-256 hash for deduplication |
+| `generationId` | string | Source generation UUID (if generated) |
+| `meta` | object | Custom metadata |
+| `createdAt` | string | ISO timestamp |
+| `updatedAt` | string | ISO timestamp |
+| `deletedAt` | string | Soft delete timestamp (null if active) |
+
+### Accessing Images
+
+Use `storageUrl` for direct image access:
+
+```html
+
+```
+
+For public CDN access, see [Live URLs](live-url.md).
+
+---
+
+## Storage Organization
+
+Images are organized in MinIO storage:
+
+```
+bucket/
+ {orgId}/
+ {projectId}/
+ uploads/ # Uploaded images
+ 2025-11/
+ image.png
+ generated/ # AI-generated images
+ 2025-11/
+ gen_abc123.png
+```
+
+---
+
+## Error Codes
+
+| HTTP Status | Code | Description |
+|-------------|------|-------------|
+| 400 | `VALIDATION_ERROR` | Invalid parameters |
+| 400 | `FILE_TOO_LARGE` | File exceeds 5MB limit |
+| 400 | `UNSUPPORTED_FILE_TYPE` | Not PNG, JPEG, or WebP |
+| 400 | `ALIAS_FORMAT_CHECK` | Alias must start with @ |
+| 401 | `UNAUTHORIZED` | Missing or invalid API key |
+| 404 | `IMAGE_NOT_FOUND` | Image or alias doesn't exist |
+| 404 | `ALIAS_NOT_FOUND` | Alias doesn't resolve to any image |
+
+---
+
+## See Also
+
+- [Basic Generation](image-generation.md) - Generate images
+- [Advanced Generation](image-generation-advanced.md) - References, aliases, flows
+- [Live URLs](live-url.md) - CDN and public access
diff --git a/docs/api/live-url.md b/docs/api/live-url.md
new file mode 100644
index 0000000..b902eb8
--- /dev/null
+++ b/docs/api/live-url.md
@@ -0,0 +1,380 @@
+# Live URL & CDN API
+
+Public CDN endpoints for image serving and live URL generation. For authenticated API, see [image-generation.md](image-generation.md).
+
+---
+
+## CDN Image Serving
+
+```
+GET /cdn/:orgSlug/:projectSlug/img/:filenameOrAlias
+```
+
+**Authentication:** None - Public endpoint
+
+Serve images by filename or project-scoped alias.
+
+**Path Parameters:**
+
+| Parameter | Description |
+|-----------|-------------|
+| `orgSlug` | Organization identifier |
+| `projectSlug` | Project identifier |
+| `filenameOrAlias` | Filename or `@alias` |
+
+**Examples:**
+
+```
+# By filename
+GET /cdn/acme/website/img/hero-background.jpg
+
+# By alias
+GET /cdn/acme/website/img/@hero
+```
+
+**Response:** Raw image bytes (not JSON)
+
+**Response Headers:**
+
+| Header | Value |
+|--------|-------|
+| `Content-Type` | `image/jpeg`, `image/png`, etc. |
+| `Content-Length` | File size in bytes |
+| `Cache-Control` | `public, max-age=31536000` (1 year) |
+| `X-Image-Id` | Image UUID |
+
+---
+
+## Live URL Generation
+
+```
+GET /cdn/:orgSlug/:projectSlug/live/:scope
+```
+
+**Authentication:** None - Public endpoint
+
+Generate images on-demand via URL parameters with automatic caching.
+
+**Path Parameters:**
+
+| Parameter | Description |
+|-----------|-------------|
+| `orgSlug` | Organization identifier |
+| `projectSlug` | Project identifier |
+| `scope` | Scope identifier (alphanumeric, hyphens, underscores) |
+
+**Query Parameters:**
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `prompt` | string | Yes | - | Image description |
+| `aspectRatio` | string | No | `"1:1"` | Aspect ratio |
+| `autoEnhance` | boolean | No | `true` | Enable prompt enhancement |
+| `template` | string | No | `"general"` | Enhancement template |
+
+**Example:**
+
+```
+GET /cdn/acme/website/live/hero-section?prompt=mountain+landscape&aspectRatio=16:9
+```
+
+**Response:** Raw image bytes
+
+### Cache Behavior
+
+**Cache HIT** - Image exists in cache:
+- Returns instantly
+- No rate limit check
+- Headers include `X-Cache-Status: HIT`
+
+**Cache MISS** - New generation:
+- Generates image using AI
+- Stores in cache
+- Counts toward rate limit
+- Headers include `X-Cache-Status: MISS`
+
+**Cache Key:** Computed from `projectId + scope + prompt + aspectRatio + autoEnhance + template`
+
+### Response Headers
+
+**Cache HIT:**
+
+| Header | Value |
+|--------|-------|
+| `Content-Type` | `image/jpeg` |
+| `Cache-Control` | `public, max-age=31536000` |
+| `X-Cache-Status` | `HIT` |
+| `X-Scope` | Scope identifier |
+| `X-Image-Id` | Image UUID |
+
+**Cache MISS:**
+
+| Header | Value |
+|--------|-------|
+| `Content-Type` | `image/jpeg` |
+| `Cache-Control` | `public, max-age=31536000` |
+| `X-Cache-Status` | `MISS` |
+| `X-Scope` | Scope identifier |
+| `X-Generation-Id` | Generation UUID |
+| `X-Image-Id` | Image UUID |
+| `X-RateLimit-Limit` | `10` |
+| `X-RateLimit-Remaining` | Remaining requests |
+| `X-RateLimit-Reset` | Seconds until reset |
+
+---
+
+## IP Rate Limiting
+
+Live URLs are rate limited by IP address:
+
+| Limit | Value |
+|-------|-------|
+| New generations | 10 per hour per IP |
+| Cache hits | Unlimited |
+
+**Note:** Only cache MISS (new generations) count toward the limit. Cache HIT requests are not limited.
+
+Rate limit headers are included on MISS responses:
+- `X-RateLimit-Limit`: Maximum requests (10)
+- `X-RateLimit-Remaining`: Remaining requests
+- `X-RateLimit-Reset`: Seconds until reset
+
+---
+
+## Scope Management
+
+Scopes organize live URL generation budgets. All scope endpoints require Project Key authentication.
+
+### Create Scope
+
+```
+POST /api/v1/live/scopes
+```
+
+**Request Body:**
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `slug` | string | Yes | - | Unique identifier |
+| `allowNewGenerations` | boolean | No | `true` | Allow new generations |
+| `newGenerationsLimit` | number | No | `30` | Max generations in scope |
+| `meta` | object | No | `{}` | Custom metadata |
+
+**Example:**
+
+```json
+{
+ "slug": "hero-section",
+ "allowNewGenerations": true,
+ "newGenerationsLimit": 50,
+ "meta": { "description": "Hero section images" }
+}
+```
+
+**Response:** `201 Created`
+
+```json
+{
+ "success": true,
+ "data": {
+ "id": "scope-123",
+ "projectId": "project-456",
+ "slug": "hero-section",
+ "allowNewGenerations": true,
+ "newGenerationsLimit": 50,
+ "currentGenerations": 0,
+ "lastGeneratedAt": null,
+ "meta": { "description": "Hero section images" },
+ "createdAt": "2025-11-28T10:00:00.000Z"
+ }
+}
+```
+
+### Lazy Scope Creation
+
+Scopes are auto-created on first live URL request if `project.allowNewLiveScopes = true`:
+
+```
+GET /cdn/acme/website/live/new-scope?prompt=...
+// Creates "new-scope" with default settings
+```
+
+### List Scopes
+
+```
+GET /api/v1/live/scopes
+```
+
+**Query Parameters:**
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `slug` | string | - | Filter by exact slug |
+| `limit` | number | `20` | Results per page (max: 100) |
+| `offset` | number | `0` | Pagination offset |
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "data": [
+ {
+ "id": "scope-123",
+ "slug": "hero-section",
+ "allowNewGenerations": true,
+ "newGenerationsLimit": 50,
+ "currentGenerations": 12,
+ "lastGeneratedAt": "2025-11-28T09:30:00.000Z"
+ }
+ ],
+ "pagination": { "limit": 20, "offset": 0, "total": 3, "hasMore": false }
+}
+```
+
+### Get Scope
+
+```
+GET /api/v1/live/scopes/:slug
+```
+
+Returns scope with statistics (currentGenerations, lastGeneratedAt).
+
+### Update Scope
+
+```
+PUT /api/v1/live/scopes/:slug
+```
+
+```json
+{
+ "allowNewGenerations": false,
+ "newGenerationsLimit": 100,
+ "meta": { "description": "Updated" }
+}
+```
+
+Changes take effect immediately for new requests.
+
+### Regenerate Scope Images
+
+```
+POST /api/v1/live/scopes/:slug/regenerate
+```
+
+**Request Body:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `imageId` | string | Specific image UUID (optional) |
+
+**Behavior:**
+- If `imageId` provided: Regenerate only that image
+- If `imageId` omitted: Regenerate all images in scope
+
+Images are regenerated with exact same parameters. IDs and URLs are preserved.
+
+### Delete Scope
+
+```
+DELETE /api/v1/live/scopes/:slug
+```
+
+**Cascade Behavior:**
+- Scope record is **hard deleted**
+- All images in scope are **hard deleted** (with MinIO cleanup)
+- Follows alias protection rules (aliased images may be kept)
+
+> **Warning:** This permanently deletes all cached images in the scope.
+
+---
+
+## Scope Settings
+
+| Setting | Type | Default | Description |
+|---------|------|---------|-------------|
+| `slug` | string | - | Unique identifier within project |
+| `allowNewGenerations` | boolean | `true` | Whether new generations are allowed |
+| `newGenerationsLimit` | number | `30` | Maximum generations in scope |
+
+When `allowNewGenerations: false`:
+- Cache HITs still work
+- New prompts return 403 error
+
+When `newGenerationsLimit` reached:
+- Cache HITs still work
+- New prompts return 429 error
+
+---
+
+## Authenticated Live Endpoint
+
+```
+GET /api/v1/live?prompt=...
+```
+
+**Authentication:** Project Key required
+
+Alternative to CDN endpoint with prompt caching by hash.
+
+**Query Parameters:**
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `prompt` | string | Yes | Image description |
+
+**Cache Behavior:**
+- Cache key: SHA-256 hash of prompt
+- Cache stored in `prompt_url_cache` table
+- Tracks hit count and last access
+
+**Response Headers:**
+- `X-Cache-Status`: `HIT` or `MISS`
+- `X-Cache-Hit-Count`: Number of cache hits (on HIT)
+
+---
+
+## Error Codes
+
+| HTTP Status | Code | Description |
+|-------------|------|-------------|
+| 400 | `SCOPE_INVALID_FORMAT` | Invalid scope slug format |
+| 403 | `SCOPE_CREATION_DISABLED` | New scope creation not allowed |
+| 404 | `ORG_NOT_FOUND` | Organization not found |
+| 404 | `PROJECT_NOT_FOUND` | Project not found |
+| 404 | `SCOPE_NOT_FOUND` | Scope does not exist |
+| 409 | `SCOPE_ALREADY_EXISTS` | Scope slug already in use |
+| 429 | `IP_RATE_LIMIT_EXCEEDED` | IP rate limit (10/hour) exceeded |
+| 429 | `SCOPE_GENERATION_LIMIT_EXCEEDED` | Scope limit reached |
+
+---
+
+## Use Cases
+
+### Dynamic Hero Images
+
+```html
+
+```
+
+First load generates, subsequent loads are cached.
+
+### Product Placeholders
+
+```html
+
+```
+
+### Blog Post Images
+
+```html
+
+```
+
+---
+
+## See Also
+
+- [Basic Generation](image-generation.md) - API-based generation
+- [Advanced Generation](image-generation-advanced.md) - References, aliases, flows
+- [Image Upload](images-upload.md) - Upload and manage images