28 KiB
Banatie API v1 - Technical Changes and Refactoring
Context
Project is in active development with no existing clients. All changes can be made without backward compatibility concerns. Priority: high-quality and correct API implementation.
1. Parameter Naming Cleanup ✅
1.1 POST /api/v1/generations
Current parameters:
assignAlias→ rename toaliasassignFlowAlias→ rename toflowAlias
Rationale: Shorter, clearer, no need for "assign" prefix when assignment is obvious from endpoint context.
Affected areas:
- Request type definitions
- Route handlers
- Service methods
- API documentation
1.2 Reference Images Auto-Detection
Parameter behavior:
referenceImagesparameter is optional- If provided (array of aliases or IDs) → use these images as references
- If empty or not provided → service must automatically parse prompt and find all aliases
Auto-detection logic:
-
Prompt parsing:
- Scan prompt text for all alias patterns (@name)
- Extract all found aliases
- Resolve each alias to actual image ID
-
Manual override:
- If
referenceImagesparameter is provided and not empty → use only specified images - Manual list takes precedence over auto-detected aliases
- If
-
Combined approach:
- If
referenceImagesprovided → add to auto-detected aliases (merge) - Remove duplicates
- Maintain order: manual references first, then auto-detected
- If
Example:
// Auto-detection (no referenceImages parameter)
{
"prompt": "A landscape based on @sunset with elements from @mountain"
// System automatically detects @sunset and @mountain
}
// Manual specification
{
"prompt": "A landscape",
"referenceImages": ["@sunset", "image-uuid-123"]
// System uses only specified images
}
// Combined
{
"prompt": "A landscape based on @sunset",
"referenceImages": ["@mountain"]
// System uses both @mountain (manual) and @sunset (auto-detected)
}
Implementation notes:
- Alias detection must use the same validation rules as alias creation
- Invalid aliases in prompt should be logged but not cause generation failure
- Maximum reference images limit still applies after combining manual + auto-detected
2. Enhanced Prompt Support - Logic Redesign
2.1 Database Schema Changes
Required schema modifications:
- Rename field:
enhancedPrompt→originalPrompt - Change field semantics:
prompt- ALWAYS contains the prompt that was used for generation (enhanced or original)originalPrompt- contains user's original input ONLY if autoEnhance was used (nullable)
Field population logic:
Case 1: autoEnhance = false
prompt = user input
originalPrompt = NULL
Case 2: autoEnhance = true
prompt = enhanced prompt (used for generation)
originalPrompt = user input (preserved)
2.2 API Response Format
Response structure:
{
"prompt": "detailed enhanced prompt...", // Always the prompt used for generation
"originalPrompt": "sunset" // Only present if enhancement was used
}
Affected endpoints:
POST /api/v1/generationsresponseGET /api/v1/generations/:idresponseGET /api/v1/generationslist response
3. Regeneration Endpoint Refactoring ✅
3.1 Endpoint Rename
Change:
- ❌ OLD:
POST /api/v1/generations/:id/retry - ✅ NEW:
POST /api/v1/generations/:id/regenerate
3.2 Remove Status Checks
- Remove
if (original.status === 'success') throw errorcheck - Remove
GENERATION_ALREADY_SUCCEEDEDerror constant - Allow regeneration for any status (pending, processing, success, failed)
3.3 Remove Retry Logic
- Remove
retryCount >= MAX_RETRY_COUNTcheck - Remove retryCount increment
- Remove
MAX_RETRY_COUNTconstant
3.4 Remove Override Parameters
- Remove
promptandaspectRatioparameters from request body - Always regenerate with exact same parameters as original
3.5 Image Update Behavior
Update existing image instead of creating new:
Preserve:
imageId(UUID remains the same)storageKey(MinIO path)storageUrlalias(if assigned)createdAt(original creation timestamp)
Update:
- Physical file in MinIO (overwrite)
fileSize(if changed)updatedAttimestamp
Generation record:
- Update
status→ processing → success/failed - Update
processingTimeMs - Keep
outputImageId(same value) - Keep
flowId(if present)
3.6 Additional Endpoint
Add for Flow:
POST /api/v1/flows/:id/regenerate- Regenerates the most recent generation in flow
- Returns
FLOW_HAS_NO_GENERATIONSerror if flow is empty - Uses parameters from the last generation in flow
4. Flow Auto-Creation (Lazy Flow Pattern)
4.1 Lazy Flow Creation Strategy
Concept:
- First request without flowId → return generated
flowIdin response, but DO NOT create in DB - Any request with valid flowId → create flow in DB if doesn't exist, add this request to flow
- If flowAlias specified in request → create flow immediately (eager creation)
4.2 Implementation Details
Flow ID Generation:
- When generation/upload has no flowId, generate UUID for potential flow
- Return this flowId in response
- Save
flowIdin generation/image record, but DO NOT create flow record
Flow Creation in DB:
Trigger: ANY request with valid flowId value
Logic:
- Check if flow record exists in DB
- Check if there are existing generations/images with this flowId
- If flow doesn't exist:
- Create flow record with provided flowId
- Include all existing records with this flowId
- Maintain chronological order based on createdAt timestamps
- If flow exists:
- Add new record to existing flow
Eager creation:
- If request includes
flowAlias→ create flow immediately - Set alias in
flow.aliasesobject
Database Schema:
generationstable already hasflowIdfield (foreign key to flows.id)imagestable already hasflowIdfield (foreign key to flows.id)- No schema changes needed
Orphan flowId handling:
- If
flowIdexists in generation/image record but not inflowstable - this is normal - Such records are called "orphans" and simply not shown in
GET /api/v1/flowslist - No cleanup job needed
- Do NOT delete such records automatically
- System works correctly with orphan flowIds until flow record is created
4.3 Endpoint Changes
Remove:
- ❌
POST /api/v1/flowsendpoint (no longer needed)
Modify responses:
POST /api/v1/generations→ always returnflowIdin response (see section 10.1)POST /api/v1/images/upload→ always returnflowIdin response (see section 10.1)
5. Upload Image Enhancements
5.1 Add Parameters
POST /api/v1/images/upload:
Parameters:
alias(optional, string) - project-scoped aliasflowAlias(optional, string) - flow-scoped alias for uploaded imageflowId(optional, string) - flow association
Behavior:
- If
flowAliasandflowIdspecified:- Ensure flow exists (or create via lazy pattern)
- Add alias to
flow.aliasesobject
- If
flowAliasWITHOUTflowId:- Apply lazy flow creation with eager pattern
- Create flow immediately, set flowAlias
- If only
aliasspecified:- Set project-scoped alias on image
5.2 Alias Conflict Resolution
Validation rules:
-
Technical aliases are forbidden:
- Cannot use:
@last,@first,@uploador any reserved technical alias - Return validation error if attempted
- Cannot use:
-
Alias override behavior:
- If alias already exists → new request has higher priority
- Alias points to new image
- Previous image loses its alias but is NOT deleted
- Same logic applies to both project aliases and flow aliases
-
Applies to both:
- Image upload with alias
- Generation with alias/flowAlias
Example:
State: Image A has alias "@hero"
Request: Upload Image B with alias "@hero"
Result:
- Image B now has alias "@hero"
- Image A loses alias (alias = NULL)
- Image A is NOT deleted
6. Image Alias Management Refactoring
6.1 Endpoint Consolidation
Remove alias handling from:
- ❌
PUT /api/v1/images/:id(body: { alias, focalPoint, meta })- Remove
aliasparameter - Keep only
focalPointandmeta
- Remove
Single method for project-scoped alias management:
- ✅
PUT /api/v1/images/:id/alias(body: { alias })- Set new alias
- Change existing alias
- Remove alias (pass
alias: null)
Rationale: Explicit intent, dedicated endpoint for alias operations, simpler validation.
6.2 Alias as Image Identifier
Support alias in path parameters:
Syntax:
- UUID:
GET /api/v1/images/550e8400-e29b-41d4-a716-446655440000 - Alias:
GET /api/v1/images/@hero @symbol distinguishes alias from UUID (UUIDs never contain@)
UUID validation: UUIDs can NEVER contain @ symbol - this guarantees no conflicts
Flow-scoped resolution:
GET /api/v1/images/@hero?flowId=uuid-123- Searches for alias
@heroin context of flowuuid-123 - Uses 3-tier precedence (technical → flow → project)
Endpoints with alias support:
GET /api/v1/images/:id_or_aliasPUT /api/v1/images/:id_or_alias(for focalPoint, meta)PUT /api/v1/images/:id_or_alias/aliasDELETE /api/v1/images/:id_or_alias
Implementation:
- Check first character of path parameter
- If starts with
@→ resolve via AliasService - If doesn't start with
@→ treat as UUID - After resolution, work with imageId as usual
6.3 CDN-style Image URLs with Alias Support
Current URL format must be changed.
New standardized URL patterns:
For all generated and uploaded images:
GET /cdn/:orgSlug/:projectSlug/img/:filenameOrAlias
For live URLs:
GET /cdn/:orgSlug/:projectSlug/live/:scope?prompt=...&aspectRatio=...
All image URLs returned by API must follow this pattern.
Resolution Logic:
- Check if
:filenameOrAliasstarts with@ - If yes → resolve alias via AliasService
- If no → search by filename/storageKey
- Return image bytes with proper content-type headers
Response Headers:
- Content-Type: image/jpeg (or appropriate MIME type)
- Cache-Control: public, max-age=31536000
- ETag: based on imageId or fileHash
URL Encoding for prompts:
- Spaces can be replaced with underscores
_for convenience - Both
prompt=beautiful%20sunsetandprompt=beautiful_sunsetare valid - System should handle both formats
Examples:
GET /cdn/acme/website/img/@hero → resolve @hero alias
GET /cdn/acme/website/img/logo.png → find by filename
GET /cdn/acme/website/img/@product-1 → resolve @product-1 alias
Error Handling:
- Alias not found → 404
- Filename not found → 404
- Multiple matches → alias takes priority over filename
7. Deletion Strategy Overhaul
7.1 Image Deletion (Hard Delete)
DELETE /api/v1/images/:id
Operations:
- Delete physical file from MinIO storage
- Delete record from
imagestable (hard delete) - Cascade: set
outputImageId = NULLin related generations - Cascade: completely remove alias entries from all
flow.aliaseswhere imageId is referenced- Remove entire key-value pairs, not just values
- Cascade: remove imageId from
generation.referencedImagesJSON arrays
Example cascade for flow.aliases:
Before: flow.aliases = { "@hero": "img-123", "@product": "img-456" }
Delete img-123
After: flow.aliases = { "@product": "img-456" }
Rationale: User wants to delete - remove completely, free storage. Alias entries are also completely removed.
7.2 Generation Deletion (Conditional)
DELETE /api/v1/generations/:id
Behavior depends on output image alias:
Case 1: Output image WITHOUT project alias
- Delete output image completely (hard delete with MinIO cleanup)
- Delete generation record (hard delete)
Case 2: Output image WITH project alias
- Keep output image (do not delete)
- Delete only generation record (hard delete)
- Set
generationId = NULLin image record
Decision Logic:
- If
outputImage.alias !== null→ keep image, delete only generation - If
outputImage.alias === null→ delete both image and generation
Rationale:
- Image with project alias is used as standalone asset, preserve it
- Image without alias was created only for this generation, delete together
No regeneration of deleted generations - deleted generations cannot be regenerated
7.3 Flow Deletion (Cascade with Alias Protection)
DELETE /api/v1/flows/:id
Operations:
- Delete flow record from DB
- Cascade: delete all generations associated with this flowId
- Cascade: delete all images associated with this flowId EXCEPT images with project alias
Detailed Cascade Logic:
For Generations:
- Delete each generation (follows conditional delete from 7.2)
- If output image has no alias → delete image
- If output image has alias → keep image, set generationId = NULL, set flowId = NULL
For Images (uploaded):
- If image has no alias → delete (with MinIO cleanup)
- If image has alias → keep, set flowId = NULL
Summary:
- Flow record → DELETE
- All generations → DELETE
- Images without alias → DELETE (with MinIO cleanup)
- Images with project alias → KEEP (unlink: flowId = NULL)
Rationale: Flow deletion removes all content except images with project aliases (used globally in project).
7.4 Transactional Delete Pattern
All delete operations must be transactional:
- Delete from MinIO storage first
- Delete from database (with cascades)
- If MinIO delete fails → rollback DB transaction
- If DB delete fails → cleanup MinIO file (or rollback if possible)
- Log all delete operations for audit trail
Principle: System must be designed so orphaned files in MinIO NEVER occur.
Database Constraints:
- ON DELETE CASCADE for appropriate foreign keys
- ON DELETE SET NULL where related records must be preserved
- Proper referential integrity
No background cleanup jobs needed - system is self-sufficient and always consistent.
8. Live URL System
8.1 Core Concept
Purpose: Permanent URLs that can be immediately inserted into HTML and work forever.
Use Case:
<img src="https://banatie.app/cdn/acme/website/live/hero-section?prompt=beautiful_sunset&aspectRatio=16:9"/>
Key Points:
- URL is constructed immediately and used permanently
- No preliminary generation through API needed
- No signed URLs or tokens in query params
- First request → generation, subsequent → cache
8.2 URL Format & Structure
URL Pattern:
/cdn/:orgSlug/:projectSlug/live/:scope?prompt=...&aspectRatio=...
URL Components:
/cdn/acme/website/live/hero-section?prompt=beautiful_sunset&aspectRatio=16:9
│ │ │ │ │
│ │ │ │ └─ Generation params (query string)
│ │ │ └─ Scope identifier
│ │ └─ "live" prefix
│ └─ Project slug
└─ Organization slug
Scope Parameter:
- Name:
scope(confirmed) - Purpose: logical separation of live URLs within project
- Format: alphanumeric + hyphens + underscores
- Any user can specify any scope (no validation/signature required)
8.3 First Request Flow
Cache MISS (first request):
- Parse orgSlug, projectSlug, scope from URL
- Compute cache key: hash(projectId + scope + prompt + params)
- Check if image exists in cache
- If NOT found:
- Check scope settings (allowNewGenerations, limit)
- Trigger image generation
- Create database records (generation, image, cache entry)
- Wait for generation to complete
- Return image bytes
Response:
- Content-Type: image/jpeg
- Cache-Control: public, max-age=31536000
- X-Cache-Status: MISS
- X-Scope: hero-section
- X-Image-Id: uuid
Cache HIT (subsequent requests):
- Same cache key lookup
- Found existing image
- Return cached image bytes immediately
Response:
- Content-Type: image/jpeg
- Cache-Control: public, max-age=31536000
- X-Cache-Status: HIT
- X-Image-Id: uuid
Generation in Progress:
- If image is not in cache but generation is already running:
- System must have internal status to track this
- Wait for generation to complete
- Return image bytes immediately when ready
- This ensures consistent behavior for concurrent requests
8.4 Scope Management
Database Table: live_scopes
Create dedicated table with fields:
id(UUID, primary key)project_id(UUID, foreign key to projects)slug(TEXT, unique within project) - used in URLallowNewGenerations(BOOLEAN, default: true) - controls if new generations can be triggerednewGenerationsLimit(INTEGER, default: 30) - max number of generations in this scopecreated_at(TIMESTAMP)updated_at(TIMESTAMP)
Scope Behavior:
allowNewGenerations:
- Controls whether new generations can be triggered in this scope
- Already generated images are ALWAYS served publicly regardless of this setting
- Default: true
newGenerationsLimit:
- Limit on number of generations in this scope
- Only affects NEW generations, does not affect regeneration
- Default: 30
Scope Creation:
- Manual: via dedicated endpoint (see below)
- Automatic: when new scope is used in live URL (if project allows)
Project-level Settings:
Add to projects table or settings:
allowNewLiveScopes(BOOLEAN, default: true) - allows creating new scopes via live URLs- If false: new scopes cannot be created via live URL
- If false: scopes can still be created via API endpoint
newLiveScopesGenerationLimit(INTEGER, default: 30) - generation limit for auto-created scopes- This value is set as
newGenerationsLimitfor newly created scopes
- This value is set as
8.5 Scope Management API
Create scope (manual):
POST /api/v1/live/scopes
Headers: X-API-Key: bnt_project_key
Body: {
"slug": "hero-section",
"allowNewGenerations": true,
"newGenerationsLimit": 50
}
List scopes:
GET /api/v1/live/scopes
Headers: X-API-Key: bnt_project_key
Response: {
"scopes": [
{
"id": "uuid",
"slug": "hero-section",
"allowNewGenerations": true,
"newGenerationsLimit": 50,
"currentGenerations": 23,
"lastGeneratedAt": "2024-01-15T10:30:00Z"
}
]
}
Get scope details:
GET /api/v1/live/scopes/:slug
Headers: X-API-Key: bnt_project_key
Response: {
"id": "uuid",
"slug": "hero-section",
"allowNewGenerations": true,
"newGenerationsLimit": 50,
"currentGenerations": 23,
"images": [...]
}
Update scope:
PUT /api/v1/live/scopes/:slug
Headers: X-API-Key: bnt_project_key
Body: {
"allowNewGenerations": false,
"newGenerationsLimit": 100
}
Regenerate scope images:
POST /api/v1/live/scopes/:slug/regenerate
Headers: X-API-Key: bnt_project_key
Body: { "imageId": "uuid" } // Optional: regenerate specific image
Response: {
"regenerated": 1,
"images": [...]
}
Delete scope:
DELETE /api/v1/live/scopes/:slug
Headers: X-API-Key: bnt_project_key
Deletion behavior: Deletes all images in this scope (follows standard image deletion with alias protection).
8.6 Security & Rate Limiting
Rate Limiting by IP:
- Aggressive limits for live URLs (e.g., 10 new generations per hour per IP)
- Separate from API key limits
- Cache hits do NOT count toward limit
- Only new generations count
Scope Quotas:
- Maximum N unique prompts per scope (newGenerationsLimit)
- After limit reached → return existing images, do not generate new
- Regeneration does not count toward limit
8.7 Caching Strategy
Cache Key:
cacheKey = hash(projectId + scope + prompt + aspectRatio + otherParams)
Cache Invalidation:
- Manual: via API endpoint regenerate
- Automatic: never (images cached forever unless explicitly regenerated)
Scope Naming: scope (confirmed)
URL Encoding:
- Prompt in query string: URL-encoded or underscores for spaces
- Both formats supported:
prompt=beautiful%20sunsetandprompt=beautiful_sunset - Scope in path: alphanumeric + hyphens + underscores
8.8 Error Handling
Detailed errors for live URLs:
- Invalid scope format → 400 "Invalid scope format. Use alphanumeric characters, hyphens, and underscores"
- New scope creation disabled → 403 "Creating new live scopes is disabled for this project"
- Generation limit exceeded → 429 "Scope generation limit exceeded. Maximum N generations per scope"
- Generation fails → 500 with retry logic
- Rate limit by IP exceeded → 429 "Rate limit exceeded. Try again in X seconds" with Retry-After header
9. Generation Modification
9.1 Update Generation Endpoint
New endpoint:
PUT /api/v1/generations/:id
Modifiable Fields:
prompt- change promptaspectRatio- change aspect ratioflowId- change/remove/add flow associationmeta- update metadata
Behavior:
Case 1: Non-generative parameters (flowId, meta)
- Simply update fields in DB
- Do NOT regenerate image
Case 2: Generative parameters (prompt, aspectRatio)
- Update fields in DB
- Automatically trigger regeneration
- Update existing image (same imageId, path, URL)
9.2 FlowId Management
FlowId handling:
flowId: null→ detach from flowflowId: "new-uuid"→ attach to different flow- If flow doesn't exist → create new flow eagerly (with this flowId)
- If flow exists → add generation to existing flow
flowId: undefined→ do not change current value
Use Case - "Detach from Flow":
- Set
flowId: nullto detach generation from flow - Output image is preserved (if has alias)
- Useful before deleting flow to protect important generations
9.3 Validation Rules
Use existing validation logic from generation creation:
- Prompt validation (existing rules)
- AspectRatio validation (existing rules)
- FlowId validation:
- If provided (not null): must be valid UUID format
- Flow does NOT need to exist (will be created eagerly if missing)
- Allow null explicitly (for detachment)
9.4 Response Format
{
"success": true,
"data": {
"id": "gen-uuid",
"prompt": "updated prompt",
"aspectRatio": "16:9",
"flowId": null,
"status": "processing", // If regeneration triggered
"regenerated": true, // Flag indicating regeneration started
"outputImage": { ... } // Current image (updates when regeneration completes)
}
}
10. Response Format Consistency
10.1 FlowId in Responses
Rule for flowId in generation and upload responses:
If request has flowId: undefined (not provided):
- Generate new flowId
- Return in response:
"flowId": "new-uuid"
If request has flowId: null (explicitly null):
- Do NOT generate flowId
- Flow is definitely not needed
- Return in response:
"flowId": null
If request has flowId: "uuid" (specific value):
- Use provided flowId
- Return in response:
"flowId": "uuid"
Examples:
// Request without flowId
POST /api/v1/generations
Body: { "prompt": "sunset" }
Response: { "flowId": "generated-uuid", ... }
// Request with explicit null
POST /api/v1/generations
Body: { "prompt": "sunset", "flowId": null }
Response: { "flowId": null, ... }
// Request with specific flowId
POST /api/v1/generations
Body: { "prompt": "sunset", "flowId": "my-flow-uuid" }
Response: { "flowId": "my-flow-uuid", ... }
11. Error Messages Updates
Remove constants:
GENERATION_ALREADY_SUCCEEDED(no longer needed)MAX_RETRY_COUNT_EXCEEDED(no longer needed)
Add constants:
SCOPE_INVALID_FORMAT- "Invalid scope format. Use alphanumeric characters, hyphens, and underscores"SCOPE_CREATION_DISABLED- "Creating new live scopes is disabled for this project"SCOPE_GENERATION_LIMIT_EXCEEDED- "Scope generation limit exceeded. Maximum {limit} generations per scope"STORAGE_DELETE_FAILED- "Failed to delete file from storage"
Update constants:
GENERATION_FAILED- include details about network/storage errorsIMAGE_NOT_FOUND- distinguish between deleted and never existed
12. Code Documentation Standards
12.1 Endpoint JSDoc Comments
Requirement: Every API endpoint must have comprehensive JSDoc comment.
Required sections:
- Purpose: What this endpoint does (one sentence)
- Logic: Brief description of how it works (2-3 key steps)
- Parameters: Description of each parameter and what it affects
- Authentication: Required authentication level
- Response: What is returned
Example format:
/**
* Generate new image from text prompt with optional reference images.
*
* Logic:
* 1. Parse prompt to auto-detect reference image aliases
* 2. Resolve all aliases (auto-detected + manual) to image IDs
* 3. Trigger AI generation with prompt and reference images
* 4. Store result with metadata and return generation record
*
* @param {string} prompt - Text description for image generation (affects: output style and content)
* @param {string[]} referenceImages - Optional aliases/IDs for reference images (affects: visual style transfer)
* @param {string} aspectRatio - Image dimensions ratio (affects: output dimensions, default: 1:1)
* @param {string} flowId - Optional flow association (affects: organization and flow-scoped aliases)
* @param {string} alias - Optional project-scoped alias (affects: image referencing across project)
* @param {string} flowAlias - Optional flow-scoped alias (affects: image referencing within flow)
* @param {boolean} autoEnhance - Enable AI prompt enhancement (affects: prompt quality and detail)
* @param {object} meta - Custom metadata (affects: searchability and organization)
*
* @authentication Project Key required
* @returns {GenerationResponse} Generation record with status and output image details
*/
router.post('/generations', ...);
Apply to:
- All route handlers in
/routes/**/*.ts - All public service methods that implement core business logic
- Complex utility functions with non-obvious behavior
Parameter descriptions must include "affects:"
- Explain what each parameter influences in the system
- Help developers understand parameter impact
- Make API more discoverable and self-documenting
Summary of Changes
Database Changes
- Rename
enhancedPrompt→originalPromptin generations table - Create
live_scopestable with fields: id, project_id, slug, allowNewGenerations, newGenerationsLimit - Add project settings: allowNewLiveScopes, newLiveScopesGenerationLimit
- Add
scopeandisLiveUrlfields to images table (optional, can use meta)
API Changes
- Rename parameters: assignAlias → alias, assignFlowAlias → flowAlias
- Make referenceImages parameter optional with auto-detection from prompt
- Rename endpoint: POST /generations/:id/retry → /generations/:id/regenerate
- Remove endpoint: POST /api/v1/flows (no longer needed)
- Add endpoint: POST /api/v1/flows/:id/regenerate
- Add endpoint: PUT /api/v1/generations/:id (modification)
- Add CDN endpoints:
- GET /cdn/:org/:project/img/:filenameOrAlias (all images)
- GET /cdn/:org/:project/live/:scope (live URLs)
- Add scope management endpoints (CRUD for live_scopes)
- Update all image URLs in API responses to use CDN format
Behavior Changes
- Lazy flow creation (create on second request or when flowAlias present)
- Alias conflict resolution (new overwrites old)
- Regenerate updates existing image (same ID, path, URL)
- Hard delete for images (with MinIO cleanup)
- Conditional delete for generations (based on alias)
- Cascade delete for flows (with alias protection)
- Live URL caching and scope management
- FlowId in responses (generate if undefined, keep if null)
- Auto-detect reference images from prompt aliases
Validation Changes
- @ symbol distinguishes aliases from UUIDs
- Technical aliases forbidden in user input
- Flow creation on-the-fly for non-existent flowIds
- Scope format validation for live URLs
Documentation Changes
- Add comprehensive JSDoc comments to all endpoints
- Include purpose, logic, parameters with "affects" descriptions
- Document authentication requirements in comments