Avatars
Generate draft AI avatars from typed traits, then approve or rename them. Drafts never enter the library without human approval; the user manages or discards unwanted drafts from the dashboard.
POST /api/v2/avatars
WRITEMCP twin: create_avatar
Generate draft avatar(s) from appearance traits + an optional scene prompt. Returns drafts that are NOT saved to the library; the user must approve them. Any traits you omit are randomized. Costs credits per image; the result reports creditsSpent and creditsRemaining. Drafts are billed once at creation, never expire, and cost nothing to keep unapproved. Each draft carries dashboardUrl, the dashboard door to that draft.
Workspace-scoped writes name workspace in the body (or as a ?workspace= query param on a bodyless DELETE); the X-Workspace-Id header is for reads only.
{
"type": "object",
"properties": {
"workspace": {
"type": "string",
"minLength": 1,
"description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
},
"traits": {
"description": "Appearance traits. Any you omit are randomized (gender-aware).",
"type": "object",
"properties": {
"gender": {
"type": "string",
"enum": [
"Female",
"Male"
]
},
"ageBand": {
"type": "string",
"enum": [
"18-19",
"20s",
"30s",
"40s",
"50s",
"60s",
"70+"
]
},
"heritage": {
"type": "string",
"enum": [
"South Asian",
"East Asian",
"Black",
"White",
"Latino",
"Middle Eastern",
"Mixed"
]
},
"faceShape": {
"type": "string",
"enum": [
"Oval",
"Round",
"Heart",
"Square",
"Angular"
]
},
"skinTone": {
"type": "string",
"enum": [
"Porcelain",
"Fair",
"Light Medium",
"Medium",
"Olive",
"Tan",
"Deep"
]
},
"skinTexture": {
"type": "string",
"enum": [
"Airbrushed clean",
"Smooth natural",
"Soft matte",
"Freckled",
"Textured pores",
"Weathered rugged"
]
},
"hairColor": {
"type": "string",
"enum": [
"Jet Black",
"Black",
"Dark Brown",
"Medium Brown",
"Light Brown",
"Auburn",
"Blonde",
"Platinum Blonde",
"Copper Red",
"Salt & Pepper",
"Gray",
"Silver",
"White"
]
},
"hairStyle": {
"type": "string",
"enum": [
"Buzz cut",
"Crew cut",
"Fade",
"Textured crop",
"Short side part",
"Slicked back",
"Quiff",
"Natural short",
"Bob cut",
"Layered cut",
"Straight & sleek",
"Soft waves",
"Beach waves",
"Loose curls",
"Tight curls",
"Soft bangs",
"Center part",
"Side part",
"Ponytail",
"Sleek bun",
"Long & flowing",
"Messy tousled"
]
},
"eyeColor": {
"type": "string",
"enum": [
"Dark Brown",
"Brown",
"Hazel",
"Green",
"Blue",
"Gray",
"Amber",
"Gray-Blue"
]
},
"bodyBuild": {
"type": "string",
"enum": [
"Slim",
"Athletic",
"Average",
"Muscular",
"Broad",
"Curvy",
"Petite",
"Plus-size"
]
},
"facialHair": {
"type": "string",
"enum": [
"Clean shaven",
"Stubble",
"Short beard",
"Full beard",
"Goatee",
"Mustache"
]
},
"makeupStyle": {
"type": "string",
"enum": [
"No makeup",
"Natural / minimal",
"Soft glam",
"Bold glam",
"Smoky eye",
"Dewy fresh"
]
},
"bustSize": {
"type": "string",
"enum": [
"Petite",
"Small",
"Medium",
"Large",
"Very Large"
]
},
"outfitStyle": {
"type": "string",
"enum": [
"Casual t-shirt",
"Button-down shirt",
"Henley shirt",
"Polo shirt",
"Hoodie",
"Sweater",
"Denim jacket",
"Leather jacket",
"Bomber jacket",
"Suit jacket",
"Tank top",
"Flannel shirt",
"Crop top",
"Off-shoulder top",
"Tube top",
"Lace camisole",
"Halter top",
"Blouse",
"Bodysuit",
"Corset top",
"Sundress",
"Wrap top",
"Cardigan",
"Spaghetti strap top",
"Bandeau top",
"Blazer",
"Kurta",
"Sari",
"Sherwani",
"Hijab with modest outfit"
]
}
}
},
"notes": {
"description": "Free-text scene / pose / mood. Anything that is NOT a fixed trait. Scene wishes are best effort, not guaranteed: view the returned image before presenting it.",
"type": "string",
"maxLength": 2000
},
"count": {
"description": "How many draft variations to generate (default 1). Every draft in a call shares this ONE trait spec; count gives look variations of the same persona, not distinct people. For distinct personas, make one call per persona.",
"type": "integer",
"minimum": 1,
"maximum": 4
},
"names": {
"description": "A name for each draft, index-aligned with count. YOU (the AI) should pick trait-appropriate names; omit to let the server auto-name. Fewer names than count is fine: the remaining drafts get server-generated names.",
"maxItems": 4,
"type": "array",
"items": {
"type": "string",
"maxLength": 100
}
},
"model": {
"description": "Image model (default gemini_flash). Higher tiers cost more credits.",
"type": "string",
"enum": [
"gemini_flash_lite",
"gemini_flash",
"gemini_pro",
"gpt_low",
"gpt_medium",
"gpt_high"
]
},
"idempotencyKey": {
"description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
"type": "string",
"maxLength": 200
}
},
"required": [
"workspace"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}{
"success": true,
"data": {
"avatars": [
{
"avatarId": "avt_9f8e7d6c5b4a3210",
"name": "Maya",
"imageUrl": "https://cdn.ghostfeed.ai/avatars/drafts/avt_9f8e7d6c5b4a3210.png",
"status": "draft",
"dashboardUrl": "https://ghostfeed.ai/w/client-a/avatar?create=1&draft=avt_9f8e7d6c5b4a3210&ref=agent"
}
],
"traits": {
"gender": "Female",
"ageBand": "20s",
"heritage": "South Asian",
"faceShape": "Oval",
"skinTone": "Medium",
"skinTexture": "Smooth",
"hairColor": "Black",
"hairStyle": "Long Wavy",
"eyeColor": "Brown",
"bodyBuild": "Slim",
"outfitStyle": "Casual"
},
"creditsSpent": 2,
"creditsRemaining": 45,
"total": 1,
"page": 1,
"pages": 1,
"nextSteps": "1 draft avatar created, NOT yet in the library. Show them to the user and call approve_avatar with the avatarId(s) they want to keep. You cannot delete drafts; the user manages or discards the rest from the dashboard. Each draft dashboardUrl opens it in the dashboard. Report the spend: 2 credits spent, 45 remaining."
}
}POST /api/v2/avatars/:id/approve
WRITEIDEMPOTENTMCP twin: approve_avatar
Approve a draft avatar by its avatarId, promoting it into the library (optionally rename). Only approve drafts the USER chose to keep. Returns dashboardUrl for the saved avatar.
Path parameters: id (from the URL).
Workspace-scoped writes name workspace in the body (or as a ?workspace= query param on a bodyless DELETE); the X-Workspace-Id header is for reads only.
{
"type": "object",
"properties": {
"avatarId": {
"type": "string",
"description": "The avatarId of the draft to approve (from create_avatar)."
},
"workspace": {
"type": "string",
"minLength": 1,
"description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
}
},
"required": [
"avatarId",
"workspace"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}{
"success": true,
"data": {
"avatarId": "avt_9f8e7d6c5b4a3210",
"name": "Maya",
"thumbnailUrl": "https://cdn.ghostfeed.ai/avatars/saved/avt_9f8e7d6c5b4a3210/thumb.png",
"status": "saved",
"dashboardUrl": "https://ghostfeed.ai/w/client-a/avatar?selected=avt_9f8e7d6c5b4a3210&ref=agent",
"nextSteps": "Approved and saved to the library. Open it: https://ghostfeed.ai/w/client-a/avatar?selected=avt_9f8e7d6c5b4a3210&ref=agent"
}
}PATCH /api/v2/avatars/:id
WRITEIDEMPOTENTMCP twin: rename_avatar
Rename an avatar (draft or saved) by its avatarId. Pure rename: does NOT save/approve a draft or change anything else.
Path parameters: id (from the URL).
Workspace-scoped writes name workspace in the body (or as a ?workspace= query param on a bodyless DELETE); the X-Workspace-Id header is for reads only.
{
"type": "object",
"properties": {
"avatarId": {
"type": "string",
"description": "The avatarId to rename (draft OR saved)."
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "The new name."
},
"workspace": {
"type": "string",
"minLength": 1,
"description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
}
},
"required": [
"avatarId",
"name",
"workspace"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}{
"success": true,
"data": {
"avatarId": "avt_1a2b3c4d5e6f7081",
"name": "Priya V2",
"status": "saved",
"dashboardUrl": "https://ghostfeed.ai/w/client-a/avatar?selected=avt_1a2b3c4d5e6f7081&ref=agent",
"nextSteps": "Renamed."
}
}GET /api/v2/avatars
READMCP twin: list_avatars
List SAVED (library) avatars in the active workspace: id, name, thumbnail. Use this to find an avatarId for rename_avatar or content generation. Unapproved drafts are separate (list_draft_avatars).
{
"type": "object",
"properties": {
"page": {
"description": "Page number (default 1).",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"limit": {
"description": "Per page (max 100). Each tool documents its own default.",
"type": "integer",
"minimum": 1,
"maximum": 100
},
"workspace": {
"description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
"type": "string"
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}{
"success": true,
"data": {
"workspace": {
"id": "6a18c36ee586c7e44e4ece67",
"name": "Client A",
"slug": "client-a"
},
"avatars": [
{
"avatarId": "avt_1a2b3c4d5e6f7081",
"name": "Priya",
"imageUrl": "https://cdn.ghostfeed.ai/avatars/saved/avt_1a2b3c4d5e6f7081/thumb.png",
"imageCount": 6,
"gender": "Female",
"creationMethod": "ai_generated",
"createdBy": "64f1c2a1b2c3d4e5f6071829",
"createdAt": "2026-06-01T08:30:00.000Z"
}
],
"total": 12,
"page": 1,
"pages": 1,
"nextSteps": "These are SAVED library avatars in the active workspace. rename_avatar works on any of them by avatarId. Pending (unapproved) drafts are separate: list_draft_avatars."
}
}GET /api/v2/avatars/drafts
READMCP twin: list_draft_avatars
List draft avatars awaiting approval in the active workspace, newest first (paginated).
{
"type": "object",
"properties": {
"page": {
"description": "Page number (default 1).",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"limit": {
"description": "Per page (max 100). Each tool documents its own default.",
"type": "integer",
"minimum": 1,
"maximum": 100
},
"workspace": {
"description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
"type": "string"
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}{
"success": true,
"data": {
"workspace": {
"id": "6a18c36ee586c7e44e4ece67",
"name": "Client A",
"slug": "client-a"
},
"drafts": [
{
"avatarId": "avt_9f8e7d6c5b4a3210",
"name": "Maya",
"imageUrl": "https://cdn.ghostfeed.ai/avatars/drafts/avt_9f8e7d6c5b4a3210.png",
"createdBy": "64f1c2a1b2c3d4e5f6071829",
"createdAt": "2026-07-18T11:00:00.000Z"
}
],
"total": 1,
"page": 1,
"pages": 1,
"nextSteps": "Approve the ones to keep (approve_avatar). You cannot delete drafts; the user manages or discards the rest from the dashboard."
}
}GET /api/v2/image-models
READMCP twin: list_image_models
List the image models create_avatar can use, with per-image credit cost, so you can offer the user options with prices.
No parameters.
{
"success": true,
"data": {
"models": [
{
"id": "gemini_flash",
"label": "Gemini Flash",
"creditCost": 1,
"provider": "google",
"isDefault": true
},
{
"id": "gpt_image",
"label": "GPT Image",
"creditCost": 3,
"provider": "openai",
"isDefault": false
}
],
"nextSteps": "Pass a chosen id as create_avatar `model` (default is the isDefault one). creditCost is per image."
}
}