docs: update admin documentation with IP rate limiting and scope error codes
Update admin.md to reflect Phase 3 Part 3 implementation changes: **Rate Limiting Section:** - Split into two subsections: API Key Rate Limiting and IP-Based Rate Limiting - API Key Rate Limiting: Expanded affected endpoints list to include scope management - IP-Based Rate Limiting: New section documenting 10/hour limit for live URLs - Separate from API key limits - Only cache MISS counts toward limit - Supports X-Forwarded-For header - In-memory store with automatic cleanup **Error Codes Section:** - Reorganized into categorized tables for better clarity - Added HTTP 409 (Conflict) status code - Added Authentication Error Codes table - Added Rate Limiting Error Codes table with both API key and IP limits - Added Live Scope Error Codes table: - SCOPE_INVALID_FORMAT (400) - SCOPE_ALREADY_EXISTS (409) - SCOPE_NOT_FOUND (404) - IMAGE_NOT_IN_SCOPE (400) **Documentation Improvements:** - All error responses include standard format - Rate limit errors include Retry-After header - Scope endpoints require project key authentication - Comprehensive notes for each section All documentation now accurately reflects current API v1 implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a92b1bf482
commit
a1c47a37f0
|
|
@ -134,6 +134,8 @@ Revoke an API key.
|
||||||
|
|
||||||
## Rate Limiting
|
## Rate Limiting
|
||||||
|
|
||||||
|
### API Key Rate Limiting
|
||||||
|
|
||||||
Rate limits apply per API key to protected endpoints.
|
Rate limits apply per API key to protected endpoints.
|
||||||
|
|
||||||
**Limits:**
|
**Limits:**
|
||||||
|
|
@ -141,10 +143,10 @@ Rate limits apply per API key to protected endpoints.
|
||||||
- **Master Keys:** No rate limit on admin endpoints
|
- **Master Keys:** No rate limit on admin endpoints
|
||||||
|
|
||||||
**Affected Endpoints:**
|
**Affected Endpoints:**
|
||||||
- All `/api/v1/generations` endpoints
|
- All `/api/v1/generations` endpoints (POST, PUT, regenerate)
|
||||||
- All `/api/v1/images` endpoints
|
- All `/api/v1/images` endpoints (POST upload, PUT)
|
||||||
- All `/api/v1/flows` endpoints
|
- All `/api/v1/flows` endpoints (PUT, regenerate)
|
||||||
- `/api/v1/live` generation endpoint
|
- All `/api/v1/live/scopes` endpoints (POST, PUT, regenerate, DELETE)
|
||||||
|
|
||||||
**Response Headers:**
|
**Response Headers:**
|
||||||
- `X-RateLimit-Limit` - Maximum requests per window
|
- `X-RateLimit-Limit` - Maximum requests per window
|
||||||
|
|
@ -157,17 +159,77 @@ Rate limits apply per API key to protected endpoints.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### IP-Based Rate Limiting (Live URLs)
|
||||||
|
|
||||||
|
Separate rate limiting for public live URL generation endpoints.
|
||||||
|
|
||||||
|
**Limits:**
|
||||||
|
- **10 new generations per hour per IP address**
|
||||||
|
- Only cache MISS (new generations) count toward limit
|
||||||
|
- Cache HIT (cached images) do NOT count toward limit
|
||||||
|
|
||||||
|
**Affected Endpoints:**
|
||||||
|
- `GET /:orgSlug/:projectSlug/live/:scope` - Public live URL generation
|
||||||
|
|
||||||
|
**Purpose:**
|
||||||
|
- Prevent abuse of public live URL endpoints
|
||||||
|
- Separate from API key limits (for authenticated endpoints)
|
||||||
|
- Does not affect API key-authenticated endpoints
|
||||||
|
|
||||||
|
**Response Headers:**
|
||||||
|
- `X-RateLimit-Limit` - Maximum requests per window (10)
|
||||||
|
- `X-RateLimit-Remaining` - Requests remaining
|
||||||
|
- `X-RateLimit-Reset` - Seconds until reset
|
||||||
|
|
||||||
|
**429 Too Many Requests:**
|
||||||
|
- Returned when IP limit exceeded
|
||||||
|
- Includes `Retry-After` header (seconds until reset)
|
||||||
|
- Error code: `IP_RATE_LIMIT_EXCEEDED`
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- Uses in-memory store with automatic cleanup
|
||||||
|
- Supports X-Forwarded-For header for proxy/load balancer setups
|
||||||
|
- IP limit resets every hour per IP address
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Error Codes
|
## Error Codes
|
||||||
|
|
||||||
|
### HTTP Status Codes
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| 401 | Unauthorized - Missing, invalid, expired, or revoked API key |
|
| 401 | Unauthorized - Missing, invalid, expired, or revoked API key |
|
||||||
| 403 | Forbidden - Insufficient permissions (master key required) |
|
| 403 | Forbidden - Insufficient permissions (master key required) |
|
||||||
| 429 | Too Many Requests - Rate limit exceeded |
|
| 409 | Conflict - Resource already exists (e.g., duplicate scope slug) |
|
||||||
|
| 429 | Too Many Requests - Rate limit exceeded (API key or IP) |
|
||||||
|
|
||||||
**Common Error Messages:**
|
### Authentication Error Codes
|
||||||
- `"Missing API key"` - No X-API-Key header provided
|
|
||||||
- `"Invalid API key"` - Key is invalid, expired, or revoked
|
| Error Code | HTTP Status | Description |
|
||||||
- `"Master key required"` - Endpoint requires master key, project key insufficient
|
|------------|-------------|-------------|
|
||||||
- `"Bootstrap not allowed"` - Keys already exist, cannot bootstrap again
|
| `MISSING_API_KEY` | 401 | No X-API-Key header provided |
|
||||||
- `"Rate limit exceeded"` - Too many requests, retry after specified time
|
| `INVALID_API_KEY` | 401 | Key is invalid, expired, or revoked |
|
||||||
|
| `MASTER_KEY_REQUIRED` | 403 | Endpoint requires master key, project key insufficient |
|
||||||
|
| `BOOTSTRAP_NOT_ALLOWED` | 403 | Keys already exist, cannot bootstrap again |
|
||||||
|
|
||||||
|
### Rate Limiting Error Codes
|
||||||
|
|
||||||
|
| Error Code | HTTP Status | Description |
|
||||||
|
|------------|-------------|-------------|
|
||||||
|
| `RATE_LIMIT_EXCEEDED` | 429 | API key rate limit exceeded (100/hour) |
|
||||||
|
| `IP_RATE_LIMIT_EXCEEDED` | 429 | IP rate limit exceeded for live URLs (10/hour) |
|
||||||
|
|
||||||
|
### Live Scope Error Codes
|
||||||
|
|
||||||
|
| Error Code | HTTP Status | Description |
|
||||||
|
|------------|-------------|-------------|
|
||||||
|
| `SCOPE_INVALID_FORMAT` | 400 | Scope slug format invalid (must be alphanumeric + hyphens + underscores) |
|
||||||
|
| `SCOPE_ALREADY_EXISTS` | 409 | Scope with this slug already exists in project |
|
||||||
|
| `SCOPE_NOT_FOUND` | 404 | Scope does not exist or access denied |
|
||||||
|
| `IMAGE_NOT_IN_SCOPE` | 400 | Image does not belong to specified scope |
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- All error responses follow the format: `{ "success": false, "error": { "message": "...", "code": "..." } }`
|
||||||
|
- Rate limit errors include `Retry-After` header with seconds until reset
|
||||||
|
- Scope management endpoints require project key authentication
|
||||||
|
|
|
||||||
|
|
@ -8,24 +8,26 @@ All endpoints require Project Key authentication via `X-API-Key` header and are
|
||||||
|
|
||||||
### POST /api/v1/generations
|
### POST /api/v1/generations
|
||||||
|
|
||||||
Create new image generation with optional reference images and flow support.
|
Create new image generation with optional reference images, aliases, and auto-enhancement.
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `prompt` - Text description for image (required)
|
- `prompt` - Text description for image (required)
|
||||||
- `referenceImages` - Array of image references (aliases or image IDs)
|
- `referenceImages` - Array of image references (aliases or image IDs)
|
||||||
- `aspectRatio` - Image aspect ratio (e.g., "16:9", "1:1")
|
- `aspectRatio` - Image aspect ratio (e.g., "16:9", "1:1", "3:2", "9:16", default: "1:1")
|
||||||
- `flowId` - Associate generation with a flow (UUID)
|
- `flowId` - Associate generation with a flow (UUID)
|
||||||
- `assignAlias` - Assign project-level alias to output image
|
- `alias` - Assign project-scoped alias to output image (@custom-name)
|
||||||
- `assignFlowAlias` - Assign flow-level alias to output image
|
- `flowAlias` - Assign flow-scoped alias to output image (requires flowId)
|
||||||
- `autoEnhance` - Enable prompt enhancement (boolean)
|
- `autoEnhance` - Enable prompt enhancement (boolean, default: false)
|
||||||
|
- `enhancementOptions` - Enhancement configuration (object, optional)
|
||||||
|
- `template` - Enhancement template: "photorealistic", "illustration", "minimalist", "sticker", "product", "comic", "general"
|
||||||
- `meta` - Custom metadata (JSON object)
|
- `meta` - Custom metadata (JSON object)
|
||||||
|
|
||||||
**Purpose:** Generate AI images with reference support and automatic alias assignment
|
**Purpose:** Generate AI images with reference support and automatic alias assignment
|
||||||
|
|
||||||
// TODO: change assignAlias to alias, assignFlowAlias to flowAlias
|
**Response:** 201 Created with generation details, status, and output image
|
||||||
// TODO: response should include enhanced prompt if used
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### GET /api/v1/generations
|
### GET /api/v1/generations
|
||||||
|
|
@ -36,10 +38,10 @@ List generations with filtering and pagination.
|
||||||
|
|
||||||
**Query Parameters:**
|
**Query Parameters:**
|
||||||
- `flowId` - Filter by flow (UUID)
|
- `flowId` - Filter by flow (UUID)
|
||||||
- `status` - Filter by status (pending/processing/success/failed)
|
- `status` - Filter by status (pending|processing|success|failed)
|
||||||
- `limit` - Results per page (default: 20, max: 100)
|
- `limit` - Results per page (default: 20, max: 100)
|
||||||
- `offset` - Pagination offset (default: 0)
|
- `offset` - Pagination offset (default: 0)
|
||||||
- `includeDeleted` - Include soft-deleted records (boolean)
|
- `includeDeleted` - Include soft-deleted records (boolean, default: false)
|
||||||
|
|
||||||
**Purpose:** Browse generation history with optional filters
|
**Purpose:** Browse generation history with optional filters
|
||||||
|
|
||||||
|
|
@ -54,23 +56,57 @@ Get single generation with full details.
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Generation UUID (path parameter)
|
- `id` - Generation UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** View complete generation details including output and reference images
|
**Purpose:** View complete generation details including output image, reference images, flow association, and timestamps
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### POST /api/v1/generations/:id/retry
|
### PUT /api/v1/generations/:id
|
||||||
|
|
||||||
Retry a failed generation.
|
Update generation parameters with automatic regeneration.
|
||||||
// TODO: the main purpose of this to regenerate an image, e.g. refresh, or reflect some shared project settings like styles or aliases. It's not for generating failed generations. Not sure we need to allow alter prompt or aspectRatio. Now it return error: "Cannot retry a generation that already succeeded" - it's not a desired behavior
|
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Generation UUID (path parameter)
|
- `id` - Generation UUID (path parameter)
|
||||||
- `prompt` - Override original prompt (optional)
|
- `prompt` - New prompt (triggers regeneration)
|
||||||
- `aspectRatio` - Override aspect ratio (optional)
|
- `aspectRatio` - New aspect ratio (triggers regeneration)
|
||||||
|
- `flowId` - Change flow association (null to detach, no regeneration)
|
||||||
|
- `meta` - Update custom metadata (no regeneration)
|
||||||
|
|
||||||
**Purpose:** Recreate a failed generation with optional parameter overrides
|
**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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -83,27 +119,19 @@ Delete generation and its output image.
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Generation UUID (path parameter)
|
- `id` - Generation UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** Remove generation record and associated output image (soft delete)
|
**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
|
||||||
|
|
||||||
Flows organize related generations into chains and support flow-scoped aliases.
|
Flows organize related generations into chains and support flow-scoped aliases. Flows are created automatically when generations or uploads specify a flowId.
|
||||||
|
|
||||||
### POST /api/v1/flows
|
|
||||||
|
|
||||||
Create new flow.
|
|
||||||
// TODO: each generation without flowId specified should start a new flow. We don't need a separate create flow endpoint
|
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `meta` - Custom metadata (JSON object, optional)
|
|
||||||
|
|
||||||
**Purpose:** Initialize a new generation chain/workflow
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### GET /api/v1/flows
|
### GET /api/v1/flows
|
||||||
|
|
||||||
|
|
@ -117,6 +145,11 @@ List all flows with pagination.
|
||||||
|
|
||||||
**Purpose:** Browse all flows with computed generation and image counts
|
**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 /api/v1/flows/:id
|
||||||
|
|
@ -128,7 +161,7 @@ Get single flow with details.
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Flow UUID (path parameter)
|
- `id` - Flow UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** View flow metadata, aliases, and computed counts
|
**Purpose:** View flow metadata, aliases, and computed counts (generationCount, imageCount)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -145,7 +178,7 @@ List all generations in a flow.
|
||||||
- `limit` - Results per page (default: 20, max: 100)
|
- `limit` - Results per page (default: 20, max: 100)
|
||||||
- `offset` - Pagination offset (default: 0)
|
- `offset` - Pagination offset (default: 0)
|
||||||
|
|
||||||
**Purpose:** View all generations associated with this flow
|
**Purpose:** View all generations associated with this flow, ordered by creation date (newest first)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -162,7 +195,7 @@ List all images in a flow.
|
||||||
- `limit` - Results per page (default: 20, max: 100)
|
- `limit` - Results per page (default: 20, max: 100)
|
||||||
- `offset` - Pagination offset (default: 0)
|
- `offset` - Pagination offset (default: 0)
|
||||||
|
|
||||||
**Purpose:** View all images (generated and uploaded) associated with this flow
|
**Purpose:** View all images (generated and uploaded) associated with this flow, ordered by creation date (newest first)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -179,8 +212,9 @@ Update flow aliases.
|
||||||
**Purpose:** Add or update flow-scoped aliases for image referencing
|
**Purpose:** Add or update flow-scoped aliases for image referencing
|
||||||
|
|
||||||
**Notes:**
|
**Notes:**
|
||||||
- Merges with existing aliases
|
- Merges with existing aliases (does not replace all)
|
||||||
- Aliases must map to valid image IDs
|
- Aliases must map to valid image IDs
|
||||||
|
- Aliases are stored in JSONB field for efficient lookups
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -192,9 +226,28 @@ Remove specific flow alias.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Flow UUID (path parameter)
|
- `id` - Flow UUID (path parameter)
|
||||||
- `alias` - Alias name to remove (path parameter)
|
- `alias` - Alias name to remove (path parameter, e.g., "@hero")
|
||||||
|
|
||||||
**Purpose:** Delete a single alias from flow's alias map
|
**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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -207,13 +260,20 @@ Delete flow.
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Flow UUID (path parameter)
|
- `id` - Flow UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** Remove flow (hard delete, generations and images remain)
|
**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
|
## Images
|
||||||
|
|
||||||
Database-tracked image management with upload, listing, and metadata updates.
|
Database-tracked image management with upload, listing, metadata updates, and deletion.
|
||||||
|
|
||||||
### POST /api/v1/images/upload
|
### POST /api/v1/images/upload
|
||||||
|
|
||||||
|
|
@ -221,20 +281,26 @@ Upload image file with database tracking.
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
**Form Parameters:**
|
**Form Parameters (multipart/form-data):**
|
||||||
- `file` - Image file (required, max 5MB, PNG/JPEG/WebP)
|
- `file` - Image file (required, max 5MB, PNG/JPEG/WebP)
|
||||||
- `alias` - Project-level alias (optional)
|
- `alias` - Project-scoped alias (optional, e.g., "@logo")
|
||||||
- `flowId` - Associate with flow (UUID, optional)
|
- `flowId` - Associate with flow (UUID, optional)
|
||||||
- `meta` - Custom metadata (JSON object, 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
|
**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:**
|
**Notes:**
|
||||||
- Creates both MinIO storage file and database entry
|
- Creates both MinIO storage file and database entry
|
||||||
- Supports PNG, JPEG, JPG, WebP formats
|
- Supports PNG, JPEG, JPG, WebP formats
|
||||||
- Maximum file size: 5MB
|
- Maximum file size: 5MB
|
||||||
|
- Eager flow creation when flowAlias is provided
|
||||||
|
|
||||||
// TODO: need to add flowAlias param
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### GET /api/v1/images
|
### GET /api/v1/images
|
||||||
|
|
@ -245,11 +311,11 @@ List images with filtering and pagination.
|
||||||
|
|
||||||
**Query Parameters:**
|
**Query Parameters:**
|
||||||
- `flowId` - Filter by flow (UUID)
|
- `flowId` - Filter by flow (UUID)
|
||||||
- `source` - Filter by source (generated/uploaded)
|
- `source` - Filter by source (generated|uploaded)
|
||||||
- `alias` - Filter by project alias
|
- `alias` - Filter by project alias (exact match)
|
||||||
- `limit` - Results per page (default: 20, max: 100)
|
- `limit` - Results per page (default: 20, max: 100)
|
||||||
- `offset` - Pagination offset (default: 0)
|
- `offset` - Pagination offset (default: 0)
|
||||||
- `includeDeleted` - Include soft-deleted records (boolean)
|
- `includeDeleted` - Include soft-deleted records (boolean, default: false)
|
||||||
|
|
||||||
**Purpose:** Browse image library with optional filters
|
**Purpose:** Browse image library with optional filters
|
||||||
|
|
||||||
|
|
@ -262,18 +328,19 @@ Resolve alias to image using 3-tier precedence.
|
||||||
**Authentication:** Project Key required
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `alias` - Alias to resolve (path parameter)
|
- `alias` - Alias to resolve (path parameter, e.g., "@hero", "@last")
|
||||||
|
|
||||||
**Query Parameters:**
|
**Query Parameters:**
|
||||||
- `flowId` - Flow context for resolution (UUID, optional)
|
- `flowId` - Flow context for resolution (UUID, optional)
|
||||||
|
|
||||||
**Purpose:** Lookup image by alias with technical → flow → project precedence
|
**Purpose:** Lookup image by alias with technical → flow → project precedence
|
||||||
|
|
||||||
**Notes:**
|
**Resolution Order:**
|
||||||
- Technical aliases: @last, @first, @upload (computed)
|
1. Technical aliases (if matches @last, @first, @upload) - computed on-the-fly
|
||||||
- Flow aliases: Scoped to specific flow
|
2. Flow aliases (if flowId provided) - looked up in flow's JSONB aliases field
|
||||||
- Project aliases: Global within project
|
3. Project aliases (global) - looked up in images.alias column
|
||||||
- Returns image record and resolution metadata (scope, flowId)
|
|
||||||
|
**Returns:** Image record with resolution metadata (imageId, scope, flowId, image details)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -286,7 +353,7 @@ Get single image by ID.
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Image UUID (path parameter)
|
- `id` - Image UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** View complete image metadata and details
|
**Purpose:** View complete image metadata including storage URLs, project/flow associations, alias, source, file metadata, focal point, and custom metadata
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -298,13 +365,15 @@ Update image metadata.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Image UUID (path parameter)
|
- `id` - Image UUID (path parameter)
|
||||||
- `alias` - Update project alias (optional)
|
- `focalPoint` - Update focal point coordinates (object: {x: 0.0-1.0, y: 0.0-1.0}, optional)
|
||||||
- `focalPoint` - Update focal point coordinates (object: {x, y}, optional)
|
|
||||||
- `meta` - Update custom metadata (JSON object, optional)
|
- `meta` - Update custom metadata (JSON object, optional)
|
||||||
|
|
||||||
**Purpose:** Modify image metadata fields
|
**Purpose:** Modify non-generative image properties
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- Alias assignment moved to separate endpoint PUT /images/:id/alias
|
||||||
|
- Focal point used for image cropping
|
||||||
|
|
||||||
// TODO: should be a way to remove alias from an image
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### PUT /api/v1/images/:id/alias
|
### PUT /api/v1/images/:id/alias
|
||||||
|
|
@ -315,67 +384,254 @@ Assign or update project-scoped alias.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Image UUID (path parameter)
|
- `id` - Image UUID (path parameter)
|
||||||
- `alias` - Alias name (string, required in request body)
|
- `alias` - Alias name (string, required in request body, e.g., "@hero-bg")
|
||||||
|
|
||||||
**Purpose:** Set project-level alias for image referencing
|
**Purpose:** Set project-level alias for image referencing
|
||||||
|
|
||||||
**Notes:**
|
**Notes:**
|
||||||
|
- Alias must start with @ symbol
|
||||||
|
- Must be unique within the project
|
||||||
- Validates alias format and reserved names
|
- Validates alias format and reserved names
|
||||||
- Checks for conflicts with existing aliases
|
- Checks for conflicts with existing aliases
|
||||||
|
- Replaces existing alias if image already has one
|
||||||
|
|
||||||
// TODO: why we need this if we can do it with `PUT /api/v1/images/:id`?
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### DELETE /api/v1/images/:id
|
### DELETE /api/v1/images/:id
|
||||||
|
|
||||||
Soft delete image.
|
Delete image with storage cleanup.
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `id` - Image UUID (path parameter)
|
- `id` - Image UUID (path parameter)
|
||||||
|
|
||||||
**Purpose:** Mark image as deleted without removing from storage
|
**Purpose:** Permanently remove image and its storage file
|
||||||
|
|
||||||
**Notes:**
|
**Notes:**
|
||||||
- Sets deletedAt timestamp
|
- Hard delete of image record (no soft delete)
|
||||||
- Image remains in storage
|
- Removes file from MinIO storage permanently
|
||||||
- Excluded from default queries
|
- Cascades to delete generation-image relationships
|
||||||
|
- Removes image from flow aliases (if present)
|
||||||
|
- Cannot be undone - use with caution
|
||||||
|
|
||||||
// TODO: why "soft" delete? did we discuss "soft" way and how to actually delete the image then?
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Live Generation
|
## CDN Endpoints
|
||||||
|
|
||||||
### GET /api/v1/live
|
Public endpoints for serving images and live URL generation without authentication.
|
||||||
|
|
||||||
Generate image with prompt-based caching.
|
### GET /cdn/:orgSlug/:projectSlug/img/:filenameOrAlias
|
||||||
|
|
||||||
**Authentication:** Project Key required
|
Serve images by filename or project-scoped alias via public CDN.
|
||||||
|
|
||||||
**Query Parameters:**
|
**Authentication:** None - Public endpoint
|
||||||
- `prompt` - Text description for image (required)
|
|
||||||
- `aspectRatio` - Image aspect ratio (optional)
|
|
||||||
|
|
||||||
**Purpose:** Generate images with intelligent caching based on prompt hash
|
**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 Format:** Raw image bytes (not JSON)
|
||||||
|
|
||||||
**Response Headers:**
|
**Response Headers:**
|
||||||
- `Content-Type` - image/jpeg (or appropriate MIME type)
|
- `Content-Type` - image/jpeg (or appropriate MIME type)
|
||||||
- `X-Cache-Status` - HIT or MISS
|
- `Content-Length` - Actual byte length
|
||||||
|
- `Cache-Control` - public, max-age=31536000 (1 year)
|
||||||
- `X-Image-Id` - Image database UUID
|
- `X-Image-Id` - Image database UUID
|
||||||
- `X-Generation-Id` - Generation UUID (on cache MISS)
|
|
||||||
- `X-Cache-Hit-Count` - Number of cache hits (on cache HIT)
|
**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
|
- `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:**
|
**Caching Behavior:**
|
||||||
- **Cache HIT:** Returns existing cached image from storage
|
- **Cache HIT:** Returns existing image instantly, no rate limit check
|
||||||
- **Cache MISS:** Generates new image, caches it, returns bytes
|
- **Cache MISS:** Generates new image, counts toward IP rate limit
|
||||||
- Cache key: SHA-256 hash of prompt
|
- Cache key computed from: prompt + aspectRatio + autoEnhance + template
|
||||||
- Cache lookup scoped to project
|
- 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
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
GET /cdn/acme/website/live/hero-section?prompt=mountain+landscape&aspectRatio=16:9
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Live Scopes Management
|
||||||
|
|
||||||
|
Authenticated endpoints for managing live URL scopes. All require Project Key authentication.
|
||||||
|
|
||||||
|
### POST /api/v1/live/scopes
|
||||||
|
|
||||||
|
Create new live scope manually with settings.
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET /api/v1/live/scopes
|
||||||
|
|
||||||
|
List all live scopes with pagination and statistics.
|
||||||
|
|
||||||
|
**Authentication:** Project Key required
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
- `slug` - Filter by exact slug match (optional)
|
||||||
|
- `limit` - Results per page (default: 20, max: 100)
|
||||||
|
- `offset` - Pagination offset (default: 0)
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
// TODO: we can't pass apikey in query param because it's assumed those images are public available on the generated pages. So we need another way to protect this endpoint and also to protect clients from using their credits
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Alias System
|
## Alias System
|
||||||
|
|
@ -398,7 +654,7 @@ The v1 API supports a 3-tier alias resolution system for referencing images.
|
||||||
- Future reserved: `@all`, `@latest`, `@oldest`, `@random`, `@next`, `@prev`, `@previous`
|
- Future reserved: `@all`, `@latest`, `@oldest`, `@random`, `@next`, `@prev`, `@previous`
|
||||||
|
|
||||||
**Flow Aliases** (Flow-scoped)
|
**Flow Aliases** (Flow-scoped)
|
||||||
- Stored in flow's aliases JSON field
|
- Stored in flow's aliases JSONB field
|
||||||
- Only accessible within flow context
|
- Only accessible within flow context
|
||||||
- Set via `PUT /api/v1/flows/:id/aliases`
|
- Set via `PUT /api/v1/flows/:id/aliases`
|
||||||
- Example: `@hero` in flow A is different from `@hero` in flow B
|
- Example: `@hero` in flow A is different from `@hero` in flow B
|
||||||
|
|
@ -406,7 +662,7 @@ The v1 API supports a 3-tier alias resolution system for referencing images.
|
||||||
**Project Aliases** (Global)
|
**Project Aliases** (Global)
|
||||||
- Unique within project
|
- Unique within project
|
||||||
- Accessible across all flows
|
- Accessible across all flows
|
||||||
- Set via `PUT /api/v1/images/:id/alias` or generation parameters
|
- Set via `PUT /api/v1/images/:id/alias` or generation/upload parameters
|
||||||
- Example: `@logo` is the same image across entire project
|
- Example: `@logo` is the same image across entire project
|
||||||
|
|
||||||
**Resolution Order:**
|
**Resolution Order:**
|
||||||
|
|
@ -422,15 +678,33 @@ The v1 API supports a 3-tier alias resolution system for referencing images.
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| 400 | Bad Request - Invalid parameters or validation failure |
|
| 400 | Bad Request - Invalid parameters or validation failure |
|
||||||
| 401 | Unauthorized - Missing, invalid, expired, or revoked API key |
|
| 401 | Unauthorized - Missing, invalid, expired, or revoked API key |
|
||||||
|
| 403 | Forbidden - Scope creation disabled or insufficient permissions |
|
||||||
| 404 | Not Found - Resource does not exist |
|
| 404 | Not Found - Resource does not exist |
|
||||||
| 429 | Too Many Requests - Rate limit exceeded |
|
| 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 |
|
| 500 | Internal Server Error - Processing or generation failure |
|
||||||
|
|
||||||
|
**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:**
|
**Common Error Messages:**
|
||||||
- `"Prompt is required"` - Missing prompt parameter
|
- `"Prompt is required"` - Missing prompt parameter
|
||||||
- `"Flow not found"` - Invalid flowId
|
|
||||||
- `"Image not found"` - Invalid image ID or alias
|
|
||||||
- `"Alias already exists"` - Alias conflict in project
|
- `"Alias already exists"` - Alias conflict in project
|
||||||
- `"Invalid aspect ratio"` - Unsupported aspect ratio format
|
- `"Invalid aspect ratio"` - Unsupported aspect ratio format
|
||||||
- `"File too large"` - Upload exceeds 5MB limit
|
- `"File too large"` - Upload exceeds 5MB limit
|
||||||
- `"alias_format_check"` - Alias must start with @ symbol (e.g., @hero not hero)
|
- `"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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue