# selfielab API Reference

> Base URL: https://selfielabstudio.com/api/
> Authentication: `Authorization: Bearer sl_your_api_key` or `X-API-Key: sl_your_api_key`
> API keys require a Pro or Studio subscription. Create keys at https://selfielabstudio.com/settings

## Agent surfaces (CLI / API / canvas parity)

External agents can perform the same creative loop as the studio canvas agent:

- **CLI**: `npm install -g selfielab` or `npx selfielab` — docs at https://selfielabstudio.com/developers/cli
- **Auth**: `SELFIELAB_API_KEY=sl_…` or `Authorization: Bearer sl_…` / `X-API-Key: sl_…`
- **Agents overview**: https://selfielabstudio.com/developers/mcp
- **Developers hub**: https://selfielabstudio.com/developers
- **Public llms.txt**: https://selfielabstudio.com/llms.txt

### Agent media endpoints

| Tool | Method | Path |
|------|--------|------|
| list tools | GET | /api/agent/tools |
| overlay text | POST | /api/agent/overlay-text |
| stitch videos | POST | /api/agent/stitch-videos |
| trim video | POST | /api/agent/trim-video |
| mute video | POST | /api/agent/mute-video |
| write shot prompt | POST | /api/agent/write-shot-prompt |
| remove background | POST | /api/photo-editor/remove-background |
| create/edit image | POST | /api/generate/{provider} |
| create video | POST | /api/generate/video |
| edit video | POST | /api/generate/video-edit |

Canvas-parity tool ids: create_image, create_image_batch, edit_image, remove_background, overlay_text, create_video, edit_video, stitch_videos, trim_video, mute_video, write_shot_prompt.

## Error Codes

| Code | Meaning |
|------|---------|
| 401 | Invalid or missing API key |
| 402 | Insufficient credits |
| 403 | Feature requires a higher subscription tier |

---

## Credits

### GET /api/user/credits

Returns your current credit balance and subscription tier.

**Query Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| includeRecent | boolean | no | Include recent credit transactions |

**Response:**
```json
{ "success": true, "credits": 142, "tier": "pro", "maxCredits": 200, "creditsResetAt": "2026-03-01T00:00:00.000Z" }
```

**Example:**
```bash
curl https://selfielabstudio.com/api/user/credits -H "Authorization: Bearer sl_your_api_key"
```

---

## API Keys

### GET /api/user/api-keys

List all API keys associated with your account.

**Response:**
```json
{ "success": true, "keys": [{ "id": "key_abc123", "name": "Production", "prefix": "sl_abc1", "createdAt": "2026-02-10T12:00:00.000Z", "lastUsedAt": "2026-02-14T08:30:00.000Z" }] }
```

### POST /api/user/api-keys

Create a new API key. The full key is only returned once.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| name | string | yes | A label for this key |

**Response:**
```json
{ "success": true, "key": "sl_live_abc123...", "keyId": "key_abc123" }
```

### DELETE /api/user/api-keys

Revoke an API key. Cannot be undone.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| keyId | string | yes | The ID of the key to revoke |

---

## Characters

### GET /api/characters

List your saved characters (reference faces for consistent generation).

**Query Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| limit | number | no | Number of characters to return (default 50) |
| offset | number | no | Offset for pagination |

**Response:**
```json
{ "success": true, "characters": [{ "id": "char_abc123", "name": "Alex", "referenceUrl": "https://selfielab.app/..." }], "total": 3, "hasMore": false }
```

### POST /api/characters

Create a new character. Provide the reference image as a URL or inline as a base64 data URI — one of the two is required.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| name | string | yes | Display name for the character |
| referenceUrl | string | conditional | URL of the reference face image. Required if referenceImage is not provided |
| referenceImage | string | conditional | Base64 data URI (e.g. "data:image/jpeg;base64,..."). Max 10 MB decoded. Supported: jpeg, png, webp, gif. Required if referenceUrl is not provided |
| description | string | no | Optional description |
| hairColor | string | no | Hair color |
| hairStyle | string | no | Hair style |
| age | string | no | Age |
| expression | string | no | Default expression |

Additional optional fields: makeup, topType, topColor, topDetails, bottomType, bottomColor, bottomDetails.

**Response:**
```json
{ "success": true, "character": { "id": "char_new123", "name": "Alex", "referenceUrl": "https://selfielab.app/references/..." } }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/characters \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Alex", "referenceImage": "data:image/jpeg;base64,/9j/4AAQ..."}'
```

### GET /api/characters/:id

Get a single character by ID, including recent generated images and videos.

**Response:**
```json
{ "success": true, "character": { "id": "char_abc123", "name": "Alex", "referenceUrl": "...", "generatedImages": [...], "generatedVideos": [...] }, "editability": { "canEdit": true }, "isFromPurchase": false }
```

### PATCH /api/characters/:id

Update a character. All fields are optional. You can update the reference image via referenceImage (base64) or referenceUrl. The old image is deleted from storage automatically. Characters published to the marketplace or sold cannot be edited (returns 403).

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| name | string | no | New display name |
| referenceUrl | string | no | New reference image URL |
| referenceImage | string | no | New reference image as base64 data URI. Max 10 MB |
| description | string | no | Updated description |
| notes | string | no | Free-text notes (max 2000 chars) |

Additional optional fields: age, expression, hairColor, hairStyle, makeup, topType, topColor, topDetails, bottomType, bottomColor, bottomDetails.

**Response:**
```json
{ "success": true, "character": { "id": "char_abc123", "name": "Alex v2", "updatedAt": "..." } }
```

### DELETE /api/characters/:id

Delete a character and all its generated images and videos. Storage files are cleaned up. Irreversible.

**Response:**
```json
{ "success": true, "deleted": { "images": 12, "videos": 3 } }
```

---

## Image Generation

### POST /api/generate/{provider}

Generate images with the specified provider. All providers share the same request/response format.

**Available Providers:**

| Slug (use in URL) | Provider | Credits | 4K |
|-------------------|----------|---------|-----|
| gemini | Gemini (Nano Banana Pro) | 3 (subscribers: 2) | yes |
| gemini-nano | Gemini Nano (Nano Banana Fast) | 1 | no |
| gpt-image-1.5 | GPT Image 1.5 | 2 | no |
| gpt-image-2 | GPT Image 2 | 2 | yes (4K = 4 credits) |
| seedream-4.5 | Seedream 4.5 | 2 | yes |
| fal-flux-2-pro | Flux 2 Pro | 2 | no |
| fal-flux-2-max | Flux 2 Max | 4 | no |
| grok-image | Grok Imagine | 1 | no |
| grok-image-pro | Grok Imagine Pro | 1 | no |
| grok-image-2.0 | Grok Image 2.0 | 1 | yes (2K) |
| kling-image-v3 | Kling Image v3 | 2 | yes |

4K doubles the credit cost on supported providers.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| formData | CharacterFormData | yes | Structured form data with subject, photography, background, accessories, and composition fields |
| referenceImage | string | no | Base64-encoded reference image for face consistency |
| simplePrompt | string | no | Direct text prompt (bypasses structured form data) |
| numImages | number | no | Number of images to generate (1-6, default 1) |
| enable4K | boolean | no | Enable 4K resolution (doubles credit cost) |

**CharacterFormData structure:**
```json
{
  "subject": { "description": "string", "expression": "string", "pose": "string", "age": "string", "hair": { "color": "string", "style": "string" }, "clothing": { "top": { "type": "string", "color": "string" }, "bottom": { "type": "string", "color": "string" } } },
  "photography": { "shotType": "string", "aspectRatio": "1:1|2:3|3:2|3:4|4:3|4:5|5:4|9:16|16:9|21:9", "angle": "string", "framing": "string", "texture": "string" },
  "background": { "setting": "string", "lighting": "string", "atmosphere": "string" },
  "accessories": { "headwear": { "type": "string" }, "eyewear": "string", "jewelry": { "earrings": "string", "necklace": "string" } },
  "composition": { "photoType": "mirror_selfie|arm_extended_selfie|selfie_camera|candid|portrait|full_body|group_photo|action_shot|profile|headshot|close_up|waist_up|three_quarter|side_profile|overhead|low_angle|fashion|editorial|lifestyle" }
}
```

**Response:**
```json
{ "success": true, "jobId": "job_abc123", "promptUsed": "{...}" }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/generate/gemini \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"formData":{"subject":{"description":"young woman","expression":"smiling"},"photography":{"shotType":"portrait","aspectRatio":"3:4"},"composition":{"photoType":"portrait"}}}'
```

Jobs are processed asynchronously. Poll GET /api/user/jobs to check status.

---

## Video Generation

### POST /api/generate/video (Standard Video)

Generate a video from a source image. Animates the image with motion based on a text prompt.

**Available Providers:**

| Slug | Provider | Credits | Premium |
|------|----------|---------|---------|
| veo-3.1 | Veo 3.1 | 2/sec | 4/sec |
| sora | Sora 2 | 2/sec | 4/sec |
| seedance | Seedance | 2/sec | 4/sec |
| wan | Wan 2.5 | 2/sec | 4/sec |
| grok-video | Grok Video | 1/sec | 4/sec |
| kling-motion | Kling Motion | 1/sec | 4/sec |

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| imageId | string | no | ID of a previously generated image (required if uploadedImageUrl/imageUrl not provided) |
| uploadedImageUrl | string | no | URL of an uploaded image (required if imageId not provided) |
| imageUrl | string | no | Alias for uploadedImageUrl — pass any image URL (e.g. from a completed job). uploadedImageUrl takes precedence if both set |
| prompt | string | yes | Motion/action prompt for the video |
| aspectRatio | string | yes | "9:16" or "16:9" |
| provider | string | no | Provider slug (see table above). Default: seedance |
| isPremium | boolean | no | Use premium quality (4 credits/sec) |
| duration | number | no | Duration in seconds. seedance: 4-12, grok-video: 1-15, kling-motion: 5-30 |
| skipEnhancement | boolean | no | Skip automatic prompt enhancement (default: false) |
| referenceVideoUrl | string | no | Reference video URL for motion copying. Required for kling-motion |

**Response:**
```json
{ "success": true, "jobId": "vjob_abc123", "promptUsed": "..." }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/generate/video \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"imageId":"img_abc123","prompt":"gentle hair movement","aspectRatio":"9:16","provider":"seedance"}'
```

### POST /api/generate/video (Avatar / Talking Head)

Generate a talking-head avatar video with voice and dialogue. Same endpoint, different providers and fields.

**Available Providers:**

| Slug | Provider | Credits | Premium |
|------|----------|---------|---------|
| kling-avatar | Kling Avatar | 1/sec | 4/sec |
| creatify-aurora | Creatify Aurora | 2/sec | 4/sec |
| veed-fabric | VEED Fabric | 2/sec | 4/sec |

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| imageId | string | no | ID of a previously generated image (required if uploadedImageUrl/imageUrl not provided) |
| uploadedImageUrl | string | no | URL of an uploaded image (required if imageId not provided) |
| imageUrl | string | no | Alias for uploadedImageUrl — pass any image URL (e.g. from a completed job). uploadedImageUrl takes precedence if both set |
| prompt | string | yes | Scene/motion prompt for the avatar video |
| dialogue | string | no | Dialogue text for the avatar to speak. Duration estimated from length (~150 chars = 10s) |
| aspectRatio | string | yes | "9:16" or "16:9" |
| provider | string | yes | "kling-avatar", "creatify-aurora", or "veed-fabric" |
| isPremium | boolean | no | Use premium quality (4 credits/sec) |
| voiceId | string | no | Voice ID from ElevenLabs or Fish Audio for TTS |
| voiceProvider | string | no | "elevenlabs" or "fish-audio" |
| voiceAudioUrl | string | no | URL of pre-generated voice audio (instead of TTS) |
| s2sAudioUrl | string | no | URL of speech-to-speech audio for voice replacement |
| s2sDuration | number | no | Duration of S2S audio in seconds (for credit calculation) |
| skipEnhancement | boolean | no | Skip prompt enhancement (default: false) |

**Response:**
```json
{ "success": true, "jobId": "vjob_def456", "promptUsed": "..." }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/generate/video \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"imageId":"img_abc123","prompt":"person talking to camera","dialogue":"Hey everyone, welcome to my channel!","aspectRatio":"9:16","provider":"kling-avatar","voiceId":"voice_abc123","voiceProvider":"elevenlabs"}'
```

If dialogue is provided without voiceId, TTS is generated automatically. Studio subscribers get a 10% discount on all video generation.

---

## Jobs

### GET /api/user/jobs

Get all active (pending/processing) image generation jobs. Poll this to check completion.

**Query Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| includeRecent | boolean | no | When true, includes jobs completed/failed in the last 5 minutes in a separate `recentJobs` array |

**Response:**
```json
{ "success": true, "jobs": [{ "id": "job_abc123", "status": "processing", "progress": "Generating image...", "createdAt": "2026-02-14T12:00:00.000Z" }], "recentJobs": [{ "id": "job_def456", "status": "completed", "imageId": "img_xyz789", "imageUrl": "https://selfielab.app/generated/...", "prompt": "...", "error": null, "createdAt": "...", "completedAt": "..." }], "subscriptionTier": "pro", "jobLimit": 3 }
```

Jobs transition: pending → processing → completed/failed. Poll every 2-3 seconds. `recentJobs` is only present when `includeRecent=true` — each entry includes `imageId` and `imageUrl` for use with the video API.

Alternatively, poll a single job via `GET /api/generate/image/{jobId}` which returns `imageId` and `imageUrl` on completion.

---

## Images

### GET /api/user/images

Get your generated images, grouped by character.

**Query Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| imageType | string | no | Filter: "image" or "video" (default: all) |
| showAll | boolean | no | Return all images across characters |

**Response:**
```json
{ "success": true, "characters": [{ "id": "char_abc", "name": "Alex", "images": [{ "id": "img_1", "url": "https://selfielab.app/..." }] }], "unassigned": [{ "id": "img_2", "url": "https://selfielab.app/..." }] }
```

### DELETE /api/user/images?id={imageId}

Permanently delete a generated image.

**Response:**
```json
{ "success": true }
```

---

## Slideshows

### POST /api/slideshows/generate

Generate a slideshow with text overlays and custom styling. Credit cost depends on provider: Grok Image (default) uses flat rate of 3 credits (1-6 slides) / 6 credits (7-10 slides). All other providers charge their per-image credit cost × number of slides.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| topic | string | yes | The topic or title for the slideshow |
| hookType | string | no | Hook type slug defining narrative format (e.g. "story-arc", "before-after", "grwm"). Sets default slide count and narrative beats. See Hook Types table below |
| characterId | string | no | Character ID for face in slideshow images |
| slideCount | number | no | Number of slides (1-10). Defaults to hook type's default if hookType is set, otherwise 6 |
| imageProvider | string | no | Image provider: "grok-image" (default), "grok-image-pro", "grok-image-2.0", "gemini", "gemini-nano", "gpt-image-1.5", "gpt-image-2", "fal-flux-2-pro", "fal-flux-2-max", "seedream-4.5", "kling-image-v3" |
| productName | string | no | Product/app name to mention in slide content and CTA |
| productDescription | string | no | Description of the product/app for accurate AI copy |
| slides | array | no | Pre-defined slides (see structure below). Length must not exceed slideCount |
| textStyle | object | no | Text overlay styling (see fields below). All fields optional, merged with defaults |

**slides[] structure:**
| Name | Type | Description |
|------|------|-------------|
| text | string | Slide text content |
| imagePrompt | string | Prompt for the slide image |

**textStyle fields (all optional):**
| Name | Type | Default | Description |
|------|------|---------|-------------|
| position | string | "bottom" | Text position: "bottom", "top", "center" |
| fontSize | number | 56 | Font size in pixels |
| fontFamily | string | "impact" | Font family |
| textColor | string | "#FFFFFF" | Text color hex |
| strokeColor | string | "#000000" | Text stroke color hex |
| strokeWidth | number | 3 | Stroke width in pixels |
| bgBarOpacity | number | 0.5 | Background bar opacity (0-1) |
| bgBarColor | string | "#000000" | Background bar color hex |
| bgBarMode | string | "full" | Background bar mode |
| textAlign | string | "center" | Text alignment |
| maxCharsPerLine | number | 20 | Max characters per line before wrapping |
| textTransform | string | "uppercase" | "uppercase", "lowercase", or "none" |
| letterSpacing | number | 0 | Letter spacing in pixels |
| verticalOffset | number | 0 | Vertical offset in pixels |
| watermark | boolean | true | Show watermark on slides |

**Response:**
```json
{ "success": true, "id": "ss_abc123", "creditsCost": 3, "pollUrl": "/api/slideshows/ss_abc123" }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/slideshows/generate \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"topic":"Morning Routine","hookType":"grwm","imageProvider":"grok-image","characterId":"char_abc123"}'
```

If slides are omitted, AI generates both text and image prompts from the topic. hookType sets the narrative format and default slide count.

### GET /api/slideshows/{id}

Get slideshow status and content. Poll until status is "completed".

**Response:**
```json
{ "success": true, "slideshow": { "id": "ss_abc123", "status": "completed", "topic": "Morning Routine", "hookType": "grwm", "slides": [{ "imageUrl": "https://selfielab.app/...", "caption": "Wake up early" }] } }
```

### GET /api/hook-types

List available hook types (narrative formats for slideshows). Requires API key.

**Response:**
```json
{ "success": true, "hookTypes": [{ "slug": "story-arc", "name": "Story Arc", "description": "Classic 6-slide emotional journey" }] }
```

**Available Hook Types:**

| Slug | Name | Description |
|------|------|-------------|
| story-arc | Story Arc | Classic 6-slide emotional journey: hook → problem → discovery → transformation → CTA. The default narrative format. |
| before-after | Before/After | Quick 4-slide transformation format: show the problem, then the dramatic result. |
| grwm | Get Ready With Me | 5-slide GRWM format: walk through a routine step by step with personality. |
| pov-story | POV Story | 5-slide immersive POV format: pull the viewer into a first-person experience. |
| tutorial | Tutorial | 6-slide how-to format: teach something step by step with a clear result. |
| listicle | Listicle | 6-slide list format: present multiple items, tips, or examples with visual variety. |
| day-in-my-life | Day in My Life | 6-slide day-in-my-life format: walk through a day with varied perspectives. |
| hot-take | Hot Take | Quick 4-slide opinion format: state a bold take, back it up, close strong. |
| product-unboxing | Product Unboxing | 5-slide unboxing format: build anticipation, reveal the product, show the reaction. |
| outfit-check | Outfit Check | 5-slide outfit showcase: present a look from multiple angles with styling details. |

Fetch the latest list programmatically: `GET /api/hook-types`


---

## Music

### POST /api/generate/music

Generate an AI music track. Cost: 2 credits (≤15s), 5 credits (30s), +3 per additional 30s.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| prompt | string | yes | Description of music style/mood |
| duration | number | yes | Duration in milliseconds (max 300000 = 5 min) |
| preset | string | no | Optional preset name |

**Response:**
```json
{ "success": true, "jobId": "music_abc123" }
```

The `jobId` is also the `musicId` used in the add-music endpoint below.

### GET /api/generate/music/{jobId}

Poll music generation status. The job progresses through pending → processing → completed.

**Response:**
```json
{ "success": true, "status": "completed", "musicUrl": "https://selfielab.app/music/abc123.mp3" }
```

| Field | Type | Description |
|-------|------|-------------|
| status | string | "pending", "processing", "completed", or "failed" |
| musicUrl | string | URL of the generated music (only when completed) |
| progress | string | Optional progress message |
| error | string | Error message (only when failed) |

---

## Video + Music

### POST /api/videos/add-music

Mix a generated music track into a video at 35% volume behind the original audio. Free — no credits charged.

**Request Body:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| videoId | string | yes | ID of a completed generated video |
| musicId | string | yes | ID of a completed music generation job |

**Response:**
```json
{ "success": true, "videoUrl": "https://selfielab.app/videos/mixed-abc123.mp4" }
```

**Example:**
```bash
curl -X POST https://selfielabstudio.com/api/videos/add-music \
  -H "Authorization: Bearer sl_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"videoId":"vid_abc123","musicId":"music_abc123"}'
```

Both video and music must be completed before mixing.

---

## Credit Costs Summary

### Image Generation
| Slug | Provider | Credits | 4K |
|------|----------|---------|-----|
| gemini | Gemini (Nano Banana Pro) | 3 (subscribers: 2) | yes |
| gemini-nano | Gemini Nano (Nano Banana Fast) | 1 | — |
| gpt-image-1.5 | GPT Image 1.5 | 2 | — |
| gpt-image-2 | GPT Image 2 | 2 | yes (4K = 4 credits) |
| seedream-4.5 | Seedream 4.5 | 2 | yes |
| fal-flux-2-pro | Flux 2 Pro | 2 | — |
| fal-flux-2-max | Flux 2 Max | 4 | — |
| grok-image | Grok Imagine | 1 | — |
| grok-image-pro | Grok Imagine Pro | 1 | — |
| grok-image-2.0 | Grok Image 2.0 | 1 | 2K: 2 |
| kling-image-v3 | Kling Image v3 | 2 | yes |

### Video Generation (Standard)
| Slug | Provider | Credits | Premium |
|------|----------|---------|---------|
| veo-3.1 | Veo 3.1 | 2/sec | 4/sec |
| sora | Sora 2 | 2/sec | 4/sec |
| seedance | Seedance | 2/sec | 4/sec |
| wan | Wan 2.5 | 2/sec | 4/sec |
| grok-video | Grok Video | 1/sec | 4/sec |
| kling-motion | Kling Motion | 1/sec | 4/sec |

### Video Generation (Avatar / Talking Head)
| Slug | Provider | Credits | Premium |
|------|----------|---------|---------|
| kling-avatar | Kling Avatar | 1/sec | 4/sec |
| creatify-aurora | Creatify Aurora | 2/sec | 4/sec |
| veed-fabric | VEED Fabric | 2/sec | 4/sec |

### Other
| Feature | Credits | Notes |
|---------|---------|-------|
| Music (≤15s) | 2 | +3 per additional 30s |
| Slideshow (Grok Image) | 3 | Flat: 3 (1-6 slides) or 6 (7-10). Other providers: per-image cost × slides |
| 3D Model | 4 | — |
| Realism Enhancement | 1 | — |

Studio subscribers receive a 10% discount on all video generation.
