{
	"openapi": "3.0.3",
	"info": {
		"title": "OhAPI Documentation",
		"description": "Public API for character management, digital twin creation, conversation rooms, text and audio replies, media generation, profile extraction, voices, and face similarity services.",
		"version": "1.0.0",
		"license": {
			"name": "Proprietary",
			"url": "https://api.oh.xyz/terms"
		}
	},
	"servers": [
		{
			"url": "https://api.oh.xyz",
			"description": "Production API Server"
		}
	],
	"security": [
		{
			"ApiKeyAuth": []
		}
	],
	"paths": {
		"/api/v2/characters/generate": {
			"post": {
				"tags": ["Characters V2"],
				"summary": "Generate an AI character asynchronously.",
				"description": "Creates a new AI character with async profile generation. Returns immediately with 'generating' status. Poll the status endpoint until status is 'ready'.",
				"operationId": "generateCharacterV2",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "// Example: distinctive accessory + tattoo via additionalDescription.\nconst response = await fetch('https://api.oh.xyz/api/v2/characters/generate', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    firstName: 'Aria',\n    lastName: 'Storm',\n    biography: 'A free-spirited artist with a passion for adventure',\n    nationality: 'American',\n    ethnicity: 'Caucasian',\n    gender: 'Female',\n    // Free-text addendum for the reference image. Rejected values surface as 400.\n    additionalDescription: 'wire-frame glasses, small dragon tattoo across the right hip'\n  })\n});\n\nif (response.status === 400) {\n  const { message } = await response.json();\n  console.error('Moderation:', message); // 'Additional details blocked by moderation'\n  return;\n}\n\nconst data = await response.json();\nconst { characterGuid } = data;\nconsole.log('Character GUID:', characterGuid);"
					},
					{
						"lang": "cURL",
						"source": "# Example: pregnancy state + scar via additionalDescription.\ncurl -X POST 'https://api.oh.xyz/api/v2/characters/generate' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\\\n    \"firstName\": \"Aria\",\\\n    \"lastName\": \"Storm\",\\\n    \"biography\": \"A free-spirited artist with a passion for adventure\",\\\n    \"nationality\": \"American\",\\\n    \"ethnicity\": \"Caucasian\",\\\n    \"gender\": \"Female\",\\\n    \"additionalDescription\": \"six months pregnant, a faint scar across her left cheekbone\"\\\n  }'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\n# Example: full-sleeve tattoo + nose piercing via additionalDescription.\nurl = 'https://api.oh.xyz/api/v2/characters/generate'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json'\n}\npayload = {\n    'firstName': 'Aria',\n    'lastName': 'Storm',\n    'biography': 'A free-spirited artist with a passion for adventure',\n    'nationality': 'American',\n    'ethnicity': 'Caucasian',\n    'gender': 'Female',\n    # Free-text addendum for the reference image. Trimmed; empty values skip moderation.\n    'additionalDescription': 'full sleeve tattoo of cherry blossoms down her left arm, septum nose piercing',\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nif response.status_code == 400:\n    print('Moderation:', response.json()['message'])\nelse:\n    data = response.json()\n    print('Character GUID:', data['characterGuid'])"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/GenerateCharacterRequest"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Character generation started. Poll status endpoint for completion.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean",
											"example": true
										},
										"characterGuid": {
											"type": "string",
											"format": "uuid",
											"description": "Character GUID for status polling and save operation"
										},
										"status": {
											"$ref": "#/components/schemas/CharacterStatus"
										},
										"character": {
											"type": "object",
											"description": "Initial character data"
										},
										"images": {
											"type": "object",
											"properties": {
												"nsfw_image": {
													"type": "string",
													"format": "uri"
												},
												"sfw_image": {
													"type": "string",
													"format": "uri"
												}
											}
										},
										"message": {
											"type": "string",
											"example": "Character generation started. Poll GET /api/v2/characters/{characterGuid}/status for progress."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Validation error - missing required fields, character under 21, or `additionalDescription` blocked by moderation (response body: `{\"message\": \"Additional details blocked by moderation\"}`)."
					},
					"401": {
						"description": "Unauthorized - invalid or missing API key"
					}
				}
			}
		},
		"/api/v2/characters/affiliate/generate-and-save": {
			"post": {
				"tags": ["Characters V2"],
				"summary": "Generate and save a character in a single async workflow.",
				"description": "Starts a combined affiliate-style character generation and save workflow. Poll the returned characterGuid with GET /api/v2/characters/{characterGuid}/status.",
				"operationId": "generateAndSaveAffiliateCharacter",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/GenerateCharacterRequest"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Character generation and save workflow started.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean"
										},
										"characterGuid": {
											"type": "string",
											"description": "Character workflow identifier.",
											"format": "uuid"
										},
										"status": {
											"$ref": "#/components/schemas/CharacterStatus"
										},
										"message": {
											"type": "string",
											"description": "Workflow status message."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v2/characters/{characterGuid}": {
			"get": {
				"tags": ["Characters V2"],
				"summary": "Get a generated character.",
				"description": "Returns the full generated character data including AI-generated profile. Use this after generation is complete (status='ready') to review before saving.",
				"operationId": "getGeneratedCharacter",
				"parameters": [
					{
						"name": "characterGuid",
						"in": "path",
						"required": true,
						"description": "Character GUID from the generate step",
						"schema": {
							"type": "string",
							"format": "uuid"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Generated character data retrieved",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"character": {
											"type": "object",
											"description": "Physical attributes, names, bio from character entity, including style (\"realistic\" or \"artificial\") as chosen at creation"
										},
										"generatedProfile": {
											"type": "object",
											"description": "AI-generated personality, interests, kinks, backstory, prompts"
										},
										"config": {
											"type": "object",
											"description": "User-provided pricing, social handles, media, voice config"
										},
										"images": {
											"type": "object",
											"properties": {
												"nsfw_image": {
													"type": "string",
													"format": "uri"
												},
												"sfw_image": {
													"type": "string",
													"format": "uri"
												}
											}
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Character not found"
					}
				}
			}
		},
		"/api/v2/characters/save": {
			"post": {
				"tags": ["Characters V2"],
				"summary": "Save a generated character asynchronously.",
				"description": "Saves a generated character. Returns immediately with 'saving' status. Poll status endpoint until status is 'saved'. Any fields provided here override what was generated.",
				"operationId": "saveCharacterV2",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v2/characters/save', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    characterGuid: 'YOUR_CHARACTER_GUID'\n  })\n});\n\nconst data = await response.json();\nconsole.log('Status:', data.status);"
					},
					{
						"lang": "cURL",
						"source": "curl -X POST 'https://api.oh.xyz/api/v2/characters/save' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\\\n    \"characterGuid\": \"YOUR_CHARACTER_GUID\"\\\n  }'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v2/characters/save'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json'\n}\npayload = {\n    'characterGuid': 'YOUR_CHARACTER_GUID'\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\ndata = response.json()\nprint('Status:', data['status'])"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/SaveCharacterRequest"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Character save started. Poll status endpoint for completion.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean",
											"example": true
										},
										"characterGuid": {
											"type": "string",
											"format": "uuid"
										},
										"status": {
											"$ref": "#/components/schemas/CharacterStatus"
										},
										"message": {
											"type": "string",
											"example": "Character save started. Poll GET /api/v2/characters/{characterGuid}/status for progress."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Validation error - missing characterGuid, character under 21, or already saved"
					},
					"401": {
						"description": "Unauthorized - invalid or missing API key"
					},
					"404": {
						"description": "Character not found"
					}
				}
			}
		},
		"/api/v2/characters/{characterGuid}/status": {
			"get": {
				"tags": ["Characters V2"],
				"summary": "Get character save/generation status.",
				"description": "Returns the current processing status of a character. Poll this endpoint after generate/save operations to track progress.",
				"operationId": "getCharacterStatus",
				"parameters": [
					{
						"name": "characterGuid",
						"in": "path",
						"required": true,
						"description": "Character GUID from the generate step",
						"schema": {
							"type": "string",
							"format": "uuid"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Character status retrieved",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"characterGuid": {
											"type": "string",
											"format": "uuid"
										},
										"status": {
											"$ref": "#/components/schemas/CharacterStatus"
										},
										"characterId": {
											"type": "integer",
											"description": "Populated when status is 'saved'"
										},
										"errorMessage": {
											"type": "string",
											"nullable": true,
											"description": "Error message if processing failed"
										},
										"createdAt": {
											"type": "string",
											"format": "date-time"
										},
										"updatedAt": {
											"type": "string",
											"format": "date-time"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Character not found"
					}
				}
			}
		},
		"/api/v1/characters/nationalities": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed nationalities.",
				"description": "Returns the list of allowed nationality values for character generation",
				"operationId": "getNationalities",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/nationalities', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/nationalities' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/nationalities'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of nationalities",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"American",
									"Brazilian",
									"Russian",
									"Colombian",
									"Ukrainian",
									"Japanese"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/ethnicities": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed ethnicities.",
				"description": "Returns the list of allowed ethnicity values for character generation",
				"operationId": "getEthnicities",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/ethnicities', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/ethnicities' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/ethnicities'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of ethnicities",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Caucasian / White",
									"Latina / Hispanic",
									"Black / African-American",
									"Asian (East Asian)"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/hair-lengths": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed hair lengths.",
				"description": "Returns the list of allowed hair length values for character generation",
				"operationId": "getHairLengths",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/hair-lengths', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/hair-lengths' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/hair-lengths'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of hair lengths",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Long (mid-back to waist)",
									"Shoulder-length",
									"Short bob",
									"Pixie cut"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/hair-colours": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed hair colours.",
				"description": "Returns the list of allowed hair colour values for character generation",
				"operationId": "getHairColours",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/hair-colours', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/hair-colours' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/hair-colours'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of hair colours",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Black",
									"Dark Brown",
									"Blonde",
									"Platinum Blonde",
									"Natural Red / Ginger"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/eye-colours": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed eye colours.",
				"description": "Returns the list of allowed eye colour values for character generation",
				"operationId": "getEyeColours",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/eye-colours', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/eye-colours' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/eye-colours'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of eye colours",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Blue",
									"Green",
									"Brown / Dark Brown",
									"Hazel",
									"Grey"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/skin-tones": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed skin tones.",
				"description": "Returns the list of allowed skin tone values for character generation",
				"operationId": "getSkinTones",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/skin-tones', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/skin-tones' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/skin-tones'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of skin tones",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Light / Fair",
									"Tan / Golden",
									"Olive / Mediterranean",
									"Caramel / Light Brown"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/breast-sizes": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed breast sizes.",
				"description": "Returns the list of allowed breast size values for character generation",
				"operationId": "getBreastSizes",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/breast-sizes', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/breast-sizes' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/breast-sizes'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of breast sizes",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"C cup",
									"D cup",
									"DD / E cup",
									"B cup",
									"Natural C"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/nipple-colours": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed nipple colours.",
				"description": "Returns the list of allowed nipple colour values for character generation",
				"operationId": "getNippleColours",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/nipple-colours', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/nipple-colours' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/nipple-colours'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of nipple colours",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Light pink",
									"Pink",
									"Rose / Medium pink",
									"Dark pink",
									"Light brown"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/breast-pertness": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed breast pertness options.",
				"description": "Returns the list of allowed breast pertness values for character generation",
				"operationId": "getBreastPertness",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/breast-pertness', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/breast-pertness' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/breast-pertness'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of breast pertness options",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Perky",
									"Firm & perky",
									"Natural perky",
									"Teardrop shape"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/vagina-hair": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed pubic hair styles.",
				"description": "Returns the list of allowed vagina hair style values for character generation",
				"operationId": "getVaginaHair",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/vagina-hair', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/vagina-hair' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/vagina-hair'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of vagina hair styles",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Completely shaved / bald",
									"Landing strip",
									"Neat triangle",
									"Natural but shaped"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/vagina-sizes": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed vagina sizes.",
				"description": "Returns the list of allowed vagina size values for character generation",
				"operationId": "getVaginaSizes",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/vagina-sizes', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/vagina-sizes' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/vagina-sizes'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of vagina sizes",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Tight",
									"Very tight",
									"Snug",
									"Average",
									"Petite tight"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/body-builds": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed body builds.",
				"description": "Returns the list of allowed body build values for character generation",
				"operationId": "getBodyBuilds",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/body-builds', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/body-builds' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/body-builds'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of body builds",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Slim",
									"Petite",
									"Athletic",
									"Curvy",
									"Hourglass",
									"Fit"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/body-shapes": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed body shapes.",
				"description": "Returns the list of allowed body shape values for character generation",
				"operationId": "getBodyShapes",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/body-shapes', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/body-shapes' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/body-shapes'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of body shapes",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Hourglass",
									"Pear",
									"Apple",
									"Rectangle",
									"Slim hourglass"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/butt-sizes": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed butt sizes.",
				"description": "Returns the list of allowed butt size values for character generation",
				"operationId": "getButtSizes",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/butt-sizes', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/butt-sizes' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/butt-sizes'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of butt sizes",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Big",
									"Round & full",
									"Bubble butt",
									"Thick",
									"Jiggly"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/orientations": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed orientations.",
				"description": "Returns the list of allowed sexual orientation values for character generation",
				"operationId": "getOrientations",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/orientations', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/orientations' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/orientations'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of orientations",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Straight",
									"Bisexual",
									"Mostly straight",
									"Bi-curious",
									"Pansexual"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/sexual-experiences": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed sexual experience values.",
				"description": "Returns the list of allowed sexual experience values for character generation",
				"operationId": "getSexualExperiences",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/sexual-experiences', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/sexual-experiences' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/sexual-experiences'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of sexual experiences",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Virgin",
									"Almost virgin",
									"Moderate",
									"Experienced",
									"Very experienced"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/kinks": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed kinks.",
				"description": "Returns the list of allowed kink values for character generation",
				"operationId": "getKinks",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/kinks', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/kinks' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/kinks'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of kinks",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Daddy kink / DDLG",
									"Light bondage",
									"Spanking",
									"Praise kink",
									"Roleplay"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/sex-positions": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed sex positions.",
				"description": "Returns the list of allowed sex position values for character generation",
				"operationId": "getSexPositions",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/sex-positions', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/sex-positions' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/sex-positions'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of sex positions",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Doggy style",
									"Missionary",
									"Cowgirl",
									"Reverse cowgirl",
									"Spooning"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/relationship-styles": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed relationship styles.",
				"description": "Returns the list of allowed relationship style values for Drupal characters",
				"operationId": "getRelationshipStyles",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/relationship-styles', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/relationship-styles' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/relationship-styles'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of relationship styles",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Monogamous girlfriend",
									"Devoted housewife",
									"Open relationship",
									"Casual FWB"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/ai-relationship-styles": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed AI relationship styles.",
				"description": "Returns the allowed AI relationship style values.",
				"operationId": "getAiRelationshipStyles",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/relationship-styles', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/relationship-styles' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/relationship-styles'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of relationship styles",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Monogamous girlfriend",
									"Devoted housewife",
									"Open relationship",
									"Casual FWB"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/personalities": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed personalities.",
				"description": "Returns the list of allowed personality values for character generation",
				"operationId": "getPersonalities",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/personalities', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/personalities' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/personalities'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of personalities",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Sweet & innocent",
									"Bratty",
									"Bubbly / genki",
									"Shy & submissive",
									"Flirty tease"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/sex-drives": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed sex drives.",
				"description": "Returns the list of allowed sex drive values for character generation",
				"operationId": "getSexDrives",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/sex-drives', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/sex-drives' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/sex-drives'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of sex drives",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": ["Very high", "High", "Average", "Moderate", "Low"]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/conversation-styles": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed conversation styles.",
				"description": "Returns the list of allowed conversation style values for character generation",
				"operationId": "getConversationStyles",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/conversation-styles', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/conversation-styles' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/conversation-styles'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of conversation styles",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Flirty & teasing",
									"Sweet & innocent",
									"Bratty & sassy",
									"Shy & soft-spoken"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/attitudes": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed attitudes.",
				"description": "Returns the list of allowed attitude values for character generation",
				"operationId": "getAttitudes",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/attitudes', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/attitudes' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/attitudes'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of attitudes",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Sweet & innocent",
									"Bratty",
									"Bubbly & cheerful",
									"Shy & submissive",
									"Flirty tease"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/relationship-statuses": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed relationship statuses.",
				"description": "Returns the list of allowed relationship status values for character generation",
				"operationId": "getRelationshipStatuses",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/relationship-statuses', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/relationship-statuses' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/relationship-statuses'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of relationship statuses",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Single",
									"In a relationship",
									"Engaged",
									"Married",
									"It's complicated"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/interests": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get allowed interests.",
				"description": "Returns the list of allowed interest values for character generation",
				"operationId": "getInterests",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/interests', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/interests' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/interests'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of interests",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"Dancing",
									"Yoga",
									"Gaming",
									"Cosplay",
									"Anime",
									"Hiking",
									"Music",
									"Gym"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/characters/jobs": {
			"get": {
				"tags": ["Characters"],
				"summary": "Get job suggestions.",
				"description": "Returns a list of suggested job titles for character generation. The job field accepts free text, so any value is valid.",
				"operationId": "getJobs",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/characters/jobs', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/characters/jobs' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/characters/jobs'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "List of job suggestions",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "string"
									}
								},
								"example": [
									"College student",
									"Content creator",
									"Nurse",
									"Flight attendant",
									"Social media influencer",
									"Barista",
									"Personal assistant",
									"Fitness trainer",
									"Teacher",
									"Yoga instructor",
									"Server",
									"Dancer",
									"Cosplayer",
									"Model",
									"Corporate professional",
									"Artist",
									"Cheerleader",
									"Gamer / streamer",
									"Photographer",
									"Software engineer",
									"Marketing manager",
									"Designer",
									"Real estate agent",
									"Entrepreneur",
									"Hair stylist",
									"Makeup artist",
									"Event planner",
									"Travel blogger",
									"Chef",
									"Actress",
									"Singer",
									"Writer",
									"Journalist",
									"Lawyer",
									"Doctor",
									"Veterinarian",
									"Psychologist",
									"Architect",
									"Interior designer",
									"Fashion designer",
									"Musician",
									"DJ",
									"Bartender",
									"Lifeguard",
									"Massage therapist",
									"Nail technician",
									"Personal shopper",
									"Tour guide",
									"Pilot",
									"Marine biologist"
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/terms/eye_color": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get eye color terms.",
				"description": "Retrieve available eye color options with their term IDs.",
				"operationId": "getEyeColorTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Green",
										"tid": "233"
									},
									{
										"name": "Brown",
										"tid": "241"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/body_type": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get body type terms.",
				"description": "Retrieve available body type options with their term IDs.",
				"operationId": "getBodyTypeTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Athletic",
										"tid": "45"
									},
									{
										"name": "Slim",
										"tid": "46"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/gender": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get gender terms.",
				"description": "Retrieve available gender options with their term IDs.",
				"operationId": "getGenderTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Female",
										"tid": "2"
									},
									{
										"name": "Male",
										"tid": "3"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/hair_color": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get hair color terms.",
				"description": "Retrieve available hair color options with their term IDs.",
				"operationId": "getHairColorTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Blonde",
										"tid": "12"
									},
									{
										"name": "Brunette",
										"tid": "13"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/orientation": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get orientation terms.",
				"description": "Retrieve available sexual orientation options with their term IDs.",
				"operationId": "getOrientationTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Straight",
										"tid": "4"
									},
									{
										"name": "Bisexual",
										"tid": "5"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/ethnicity": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get ethnicity terms.",
				"description": "Retrieve available ethnicity options with their term IDs.",
				"operationId": "getEthnicityTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Asian",
										"tid": "15"
									},
									{
										"name": "Caucasian",
										"tid": "16"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/breast_size": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get breast size terms.",
				"description": "Retrieve available breast size options with their term IDs.",
				"operationId": "getBreastSizeTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Small",
										"tid": "201"
									},
									{
										"name": "Medium",
										"tid": "202"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/interests": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get interests terms.",
				"description": "Retrieve available interests and passions with their term IDs.",
				"operationId": "getInterestsTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Art",
										"tid": "90"
									},
									{
										"name": "Music",
										"tid": "91"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/kinks": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get kinks terms.",
				"description": "Retrieve available kinks and preferences with their term IDs.",
				"operationId": "getKinksTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Preference 1",
										"tid": "301"
									},
									{
										"name": "Preference 2",
										"tid": "302"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/personality": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get personality terms.",
				"description": "Retrieve available personality trait options with their term IDs.",
				"operationId": "getPersonalityTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Adventurous",
										"tid": "120"
									},
									{
										"name": "Creative",
										"tid": "121"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/terms/traits": {
			"get": {
				"tags": ["Taxonomy"],
				"summary": "Get traits terms.",
				"description": "Retrieve available traits and characteristics with their term IDs.",
				"operationId": "getTraitsTerms",
				"responses": {
					"200": {
						"description": "Terms retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"name": {
												"type": "string"
											},
											"tid": {
												"type": "string"
											}
										}
									}
								},
								"example": [
									{
										"name": "Confident",
										"tid": "401"
									},
									{
										"name": "Friendly",
										"tid": "402"
									}
								]
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/digital-twins/check-celebrity": {
			"post": {
				"tags": ["Digital Twins"],
				"summary": "Check if an image contains a celebrity likeness.",
				"description": "Check if an image contains a celebrity likeness.",
				"operationId": "checkDigitalTwinCelebrityLikeness",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["imageUrl"],
								"properties": {
									"imageUrl": {
										"type": "string",
										"description": "Image to check.",
										"format": "uri"
									}
								}
							},
							"example": {
								"imageUrl": "https://cdn.example.com/reference.jpg"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"isCelebrity": {
											"type": "boolean"
										},
										"confidence": {
											"type": "number",
											"format": "float"
										},
										"reason": {
											"type": "string",
											"description": "Optional result explanation."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/digital-twins": {
			"post": {
				"tags": ["Digital Twins"],
				"summary": "Create a digital twin.",
				"description": "Creates a new digital twin character. Processing happens asynchronously. Poll the status endpoint to track completion.",
				"operationId": "createDigitalTwin",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateDigitalTwinRequest"
							},
							"example": {
								"name": "Luna Starfire",
								"alias": "Luna",
								"dateOfBirth": "1998-06-15",
								"job": "Digital Artist",
								"location": "Los Angeles, CA",
								"gender": "Female",
								"orientation": "Bisexual",
								"hairColour": "Blonde",
								"eyeColour": "Blue",
								"bodyType": "Athletic",
								"referenceImageUrl": "https://cdn.example.com/images/reference.jpg",
								"contentLevel": "Sexy",
								"bio": "A creative digital artist who loves exploring new worlds.",
								"personality": ["Playful", "Creative", "Confident"],
								"interests": ["Art", "Gaming", "Music"]
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Digital twin creation started. Poll status endpoint for completion.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean",
											"example": true
										},
										"digitalTwinId": {
											"type": "string",
											"format": "uuid",
											"description": "Digital twin UUID for status polling"
										},
										"status": {
											"$ref": "#/components/schemas/DigitalTwinStatus"
										},
										"message": {
											"type": "string",
											"example": "Digital twin creation started. Poll GET /api/v1/digital-twins/status/{digitalTwinId} for progress."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Validation error - missing required fields or character under 21"
					},
					"401": {
						"description": "Unauthorized - invalid or missing API key"
					},
					"500": {
						"description": "Server error - image download failed or database error"
					}
				}
			}
		},
		"/api/v1/digital-twins/{digitalTwinId}": {
			"patch": {
				"tags": ["Digital Twins"],
				"summary": "Update a digital twin.",
				"description": "Updates a digital twin. All create fields can be updated except immutable ownership fields, depending on partner permissions.",
				"operationId": "updateDigitalTwin",
				"parameters": [
					{
						"name": "digitalTwinId",
						"in": "path",
						"required": true,
						"description": "Digital twin identifier returned by the create endpoint.",
						"schema": {
							"type": "string",
							"format": "uuid"
						}
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/UpdateDigitalTwinRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Digital twin updated successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean",
											"example": true
										},
										"digitalTwinId": {
											"type": "string",
											"format": "uuid"
										},
										"characterId": {
											"type": "integer"
										},
										"updatedFields": {
											"type": "array",
											"items": {
												"type": "string"
											},
											"example": ["bio", "hairColour", "interests"]
										},
										"message": {
											"type": "string",
											"example": "Digital twin updated successfully. 3 field(s) modified."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid data - age under 21 or invalid values"
					},
					"401": {
						"description": "Unauthorized - invalid or missing API key"
					},
					"404": {
						"description": "Digital twin not found"
					},
					"500": {
						"description": "Server error - image download failed or database error"
					}
				}
			},
			"get": {
				"tags": ["Digital Twins"],
				"summary": "Get a digital twin by legacy character ID.",
				"description": "Get a digital twin by legacy character ID.",
				"operationId": "getDigitalTwinByCharacterId",
				"parameters": [
					{
						"name": "digitalTwinId",
						"in": "path",
						"required": true,
						"description": "Legacy saved character identifier used to retrieve the digital twin.",
						"schema": {
							"type": "string"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"additionalProperties": true
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Digital twin not found."
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/digital-twins/{digitalTwinId}/profile-image-with-cover-video": {
			"post": {
				"tags": ["Digital Twins"],
				"summary": "Save a profile image and generate a cover video.",
				"description": "Save a profile image and generate a cover video.",
				"operationId": "saveDigitalTwinProfileImageWithCoverVideo",
				"parameters": [
					{
						"name": "digitalTwinId",
						"in": "path",
						"required": true,
						"description": "Digital twin identifier.",
						"schema": {
							"type": "string",
							"format": "uuid"
						}
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["profileImageUrl"],
								"properties": {
									"profileImageUrl": {
										"type": "string",
										"description": "Profile image URL to save.",
										"format": "uri"
									},
									"profileImagePrompts": {
										"type": "array",
										"items": {
											"$ref": "#/components/schemas/ProfileImagePromptItem"
										}
									}
								}
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Profile image saved and cover video generation started.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AsyncGenerationResponse"
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/digital-twins/status/{digitalTwinId}": {
			"get": {
				"tags": ["Digital Twins"],
				"summary": "Get digital twin status.",
				"description": "Returns the current processing status of a digital twin. Poll this endpoint after create/update operations to track progress. When status is 'active', the characterId field is populated and can be used for subsequent update calls.",
				"operationId": "getDigitalTwinStatus",
				"parameters": [
					{
						"name": "digitalTwinId",
						"in": "path",
						"required": true,
						"description": "Digital Twin UUID returned from the create endpoint",
						"schema": {
							"type": "string",
							"format": "uuid"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Digital twin status retrieved",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"digitalTwinId": {
											"type": "string",
											"format": "uuid"
										},
										"status": {
											"$ref": "#/components/schemas/DigitalTwinStatus"
										},
										"characterId": {
											"type": "integer",
											"description": "Populated when status is 'active' - use this for update calls",
											"nullable": true
										},
										"errorMessage": {
											"type": "string",
											"nullable": true,
											"description": "Error message if processing failed"
										},
										"createdAt": {
											"type": "string",
											"format": "date-time"
										},
										"updatedAt": {
											"type": "string",
											"format": "date-time"
										}
									}
								},
								"examples": {
									"pending": {
										"summary": "Still processing",
										"value": {
											"digitalTwinId": "550e8400-e29b-41d4-a716-446655440000",
											"status": "pending",
											"characterId": null,
											"errorMessage": null
										}
									},
									"active": {
										"summary": "Ready to use",
										"value": {
											"digitalTwinId": "550e8400-e29b-41d4-a716-446655440000",
											"status": "active",
											"characterId": 12345,
											"errorMessage": null
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Digital twin not found"
					}
				}
			}
		},
		"/api/v1/digital-twins/customer-digital-twins": {
			"get": {
				"tags": ["Customer Library"],
				"summary": "Get digital twins for the authenticated customer.",
				"description": "Returns all saved digital twins for the authenticated customer",
				"operationId": "getCustomerDigitalTwins",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/digital-twins/customer-digital-twins', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/digital-twins/customer-digital-twins' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/digital-twins/customer-digital-twins'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "Customer digital twins returned successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean"
										},
										"digitalTwins": {
											"type": "array",
											"items": {
												"type": "object",
												"properties": {
													"id": {
														"type": "string",
														"format": "uuid"
													},
													"characterId": {
														"type": "number"
													},
													"name": {
														"type": "string"
													},
													"alias": {
														"type": "string"
													},
													"status": {
														"type": "string"
													},
													"profilePhotoUrl": {
														"type": "string",
														"description": "Signed image URL"
													}
												}
											}
										}
									}
								},
								"example": {
									"success": true,
									"digitalTwins": [
										{
											"id": "a47a1352-af8a-4e4a-9b2f-1e9a180ef984",
											"characterId": 9371,
											"name": "Lisa Stunner",
											"alias": "lisa",
											"status": "active",
											"profilePhotoUrl": "<signed-s3-url>"
										}
									]
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/customer-library": {
			"get": {
				"tags": ["Customer Library"],
				"summary": "Get saved characters and digital twins for the authenticated customer.",
				"description": "Returns all saved characters and digital twins for the authenticated customer",
				"operationId": "getCustomerLibrary",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/customer-library', {\n  method: 'GET',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY'\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/customer-library' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nurl = 'https://api.oh.xyz/api/v1/customer-library'\nheaders = {\n    'x-api-key': 'YOUR_API_KEY'\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
					}
				],
				"responses": {
					"200": {
						"description": "Customer library returned successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"success": {
											"type": "boolean"
										},
										"characters": {
											"type": "array",
											"items": {
												"type": "object"
											}
										},
										"digitalTwins": {
											"type": "array",
											"items": {
												"type": "object"
											}
										}
									}
								},
								"example": {
									"success": true,
									"characters": [
										{
											"cid": 8671,
											"firstName": "Rachel",
											"lastName": "Walton",
											"image_url": "<signed-s3-url>"
										}
									],
									"digitalTwins": [
										{
											"id": "a47a1352-af8a-4e4a-9b2f-1e9a180ef984",
											"characterId": 9371,
											"name": "Lisa Stunner",
											"alias": "lisa",
											"status": "active",
											"profilePhotoUrl": "<signed-s3-url>"
										}
									]
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized - invalid API key"
					}
				}
			}
		},
		"/api/v1/rooms": {
			"post": {
				"tags": ["Rooms"],
				"summary": "Create a room.",
				"description": "Creates a room binding the calling client, a user, a character, products, and conversation history. Creating a room deactivates sibling rooms for the same user and character.",
				"operationId": "createRoom",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/rooms', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'x-api-key': 'YOUR_API_KEY',\n    \n  },\n  body: JSON.stringify({\n    user_gender: 'male',\n    character_id: '154'\n  })\n});\n\nif (!response.ok) {\n  throw new Error(`Request failed: ${response.status}`);\n}\n\nconst data = await response.json();\nconsole.log(data);"
					},
					{
						"lang": "cURL",
						"source": "curl -X POST 'https://api.oh.xyz/api/v1/rooms' \\\n  -H 'Content-Type: application/json' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -d '{\n    \"user_gender\": \"male\",\n    \"character_id\": \"154\"\n  }'"
					},
					{
						"lang": "Python",
						"source": "import requests\nimport json\n\nurl = 'https://api.oh.xyz/api/v1/rooms'\nheaders = {\n    'Content-Type': 'application/json',\n    'x-api-key': 'YOUR_API_KEY',\n    \n}\ndata = {\n    'user_gender': 'male',\n    'character_id': '154'\n}\n\nresponse = requests.post(url, headers=headers, json=data)\nresponse.raise_for_status()\nprint(response.json())"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["character_id"],
								"properties": {
									"character_id": {
										"type": "string",
										"description": "Character identifier."
									},
									"user_id": {
										"type": "string",
										"description": "Client-side user identifier."
									},
									"user": {
										"$ref": "#/components/schemas/User"
									},
									"ip": {
										"type": "string",
										"description": "User IP address, if available."
									},
									"user_gender": {
										"type": "string",
										"description": "Legacy user gender field. Maps to user.gender."
									}
								}
							},
							"example": {
								"character_id": "12345",
								"user_id": "user_789",
								"user": {
									"name": "Alex",
									"gender": "male"
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Room created successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"room_id": {
											"type": "string",
											"description": "Created room identifier."
										}
									}
								},
								"example": {
									"room_id": "room_123"
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/text": {
			"post": {
				"tags": ["Text"],
				"summary": "Generate a synchronous text reply.",
				"description": "Generates a text reply using either room or direct character context.",
				"operationId": "generateText",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"description": "Provide room_id for room-aware generation, or character_id with optional history and user.",
								"anyOf": [
									{
										"required": ["room_id"]
									},
									{
										"required": ["character_id"]
									}
								],
								"properties": {
									"room_id": {
										"type": "string",
										"description": "Conversation room identifier."
									},
									"character_id": {
										"type": "string",
										"description": "Character identifier."
									},
									"history": {
										"type": "array",
										"items": {
											"$ref": "#/components/schemas/HistoryItem"
										}
									},
									"user": {
										"$ref": "#/components/schemas/User"
									},
									"prompt": {
										"type": "string",
										"description": "User prompt. May be empty for an opening-message flow."
									},
									"audio_input_url": {
										"type": "string",
										"description": "URL of a user voice note. Carry its transcript in prompt.",
										"format": "uri"
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Text reply generated successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"content": {
											"type": "string",
											"description": "Generated reply."
										},
										"job_id": {
											"type": "string",
											"description": "Associated job identifier."
										},
										"message_id": {
											"type": "string",
											"description": "Created message identifier."
										},
										"tool_call": {
											"type": "object",
											"nullable": true,
											"additionalProperties": true
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/audio/notes": {
			"post": {
				"tags": ["Audio"],
				"summary": "Generate a synchronous spoken reply for a room or character context.",
				"description": "Generates a spoken reply. Room requests use the room character's configured voice.",
				"operationId": "generateAudio",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"description": "Provide room_id for room-aware generation, or character_id with optional history and user.",
								"anyOf": [
									{
										"required": ["room_id"]
									},
									{
										"required": ["character_id"]
									}
								],
								"properties": {
									"room_id": {
										"type": "string",
										"description": "Conversation room identifier."
									},
									"character_id": {
										"type": "string",
										"description": "Character identifier."
									},
									"history": {
										"type": "array",
										"items": {
											"$ref": "#/components/schemas/HistoryItem"
										}
									},
									"user": {
										"$ref": "#/components/schemas/User"
									},
									"prompt": {
										"type": "string",
										"description": "User prompt. May be empty for an opening-message flow."
									},
									"audio_input_url": {
										"type": "string",
										"description": "URL of a user voice note. Carry its transcript in prompt.",
										"format": "uri"
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Audio reply generated successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"url": {
											"type": "string",
											"description": "Generated audio URL.",
											"format": "uri"
										},
										"job_id": {
											"type": "string",
											"description": "Associated job identifier."
										},
										"message_id": {
											"type": "string",
											"description": "Created message identifier."
										},
										"content": {
											"type": "string",
											"description": "Generated reply text."
										},
										"tool_call": {
											"type": "object",
											"nullable": true,
											"additionalProperties": true
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/jobs/{job_id}/status": {
			"get": {
				"tags": ["Jobs"],
				"summary": "Poll an async job's status and result URL.",
				"description": "Poll after image or video generation until completed or failed. Poll again to refresh expired result URLs.",
				"operationId": "getJobStatus",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "async function pollJob(jobId, apiKey, maxAttempts = 60) {\n  for (let i = 0; i < maxAttempts; i++) {\n    const response = await fetch(\n      `https://api.oh.xyz/api/v1/jobs/${jobId}/status`,\n      { headers: { 'x-api-key': apiKey } }\n    );\n    const data = await response.json();\n\n    if (data.status === 'completed') {\n      console.log('Download URL:', data.url);\n      return data;\n    }\n    if (data.status === 'failed') {\n      throw new Error(data.error || 'Job failed');\n    }\n\n    await new Promise(r => setTimeout(r, 3000));\n  }\n  throw new Error('Polling timed out');\n}"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/jobs/{job_id}/status' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests\nimport time\n\ndef poll_job(job_id, api_key, max_attempts=60):\n    url = f'https://api.oh.xyz/api/v1/jobs/{job_id}/status'\n    headers = {'x-api-key': api_key}\n\n    for _ in range(max_attempts):\n        data = requests.get(url, headers=headers).json()\n\n        if data['status'] == 'completed':\n            print('Download URL:', data['url'])\n            return data\n        if data['status'] == 'failed':\n            raise Exception(data.get('error', 'Job failed'))\n\n        time.sleep(3)\n\n    raise TimeoutError('Polling timed out')"
					}
				],
				"parameters": [
					{
						"name": "job_id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The job ID returned from image generation request"
					}
				],
				"responses": {
					"200": {
						"description": "Job status retrieved successfully",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/JobStatus"
								},
								"examples": {
									"queued": {
										"summary": "Job is queued",
										"value": {
											"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
											"status": "queued",
											"url": null,
											"results": null,
											"error": null
										}
									},
									"processing": {
										"summary": "Job is being processed",
										"value": {
											"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
											"status": "processing",
											"url": null,
											"results": null,
											"error": null
										}
									},
									"completed": {
										"summary": "Job completed successfully",
										"value": {
											"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
											"status": "completed",
											"url": "https://s3.amazonaws.com/bucket/generated/image/154/2026-04/a1b2c3d4.png?X-Amz-...",
											"results": {
												"image_prompt": "A photograph of the character at sunset...",
												"detected_level": 1,
												"style": "glam",
												"style_applied": true,
												"position": null,
												"position_source": null,
												"background": null,
												"wardrobe": null
											},
											"error": null
										}
									},
									"failed": {
										"summary": "Job failed",
										"value": {
											"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
											"status": "failed",
											"url": null,
											"results": null,
											"error": "Generation failed: timeout"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/images": {
			"post": {
				"tags": ["Images"],
				"summary": "Generate an image asynchronously. Supports direct character generation or room-aware generation.",
				"description": "Use character_id for direct generation or room_id for history-aware generation. Poll the returned job until complete. With prompt_enhancement enabled, the optional style (\"glam\" by default, or \"amateur\") picks the look of the picture at every content level, and an explicit (level 4) request is rendered around the sexual position detected from your prompt — fix it with the optional position field. A character created with style \"artificial\" always renders in its own artificial look and ignores the request's style. See \"Image styles and explicit-level rendering\" above.",
				"operationId": "generateImage",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "const response = await fetch('https://api.oh.xyz/api/v1/images', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'x-api-key': 'YOUR_API_KEY'\n  },\n  body: JSON.stringify({\n    character_id: '154',\n    prompt: 'A stunning selfie at sunset on the beach',\n    prompt_enhancement: true,\n    style: 'glam', // or 'amateur'; only read when prompt_enhancement is true\n    resolution: '9:16'\n  })\n});\n\nconst data = await response.json();\nconst { job_id, presigned_url } = data;\nconsole.log('Job ID:', job_id);\nconsole.log('Download URL:', presigned_url);"
					},
					{
						"lang": "cURL",
						"source": "curl -X POST 'https://api.oh.xyz/api/v1/images' \\\n  -H 'Content-Type: application/json' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -d '{\n    \"character_id\": \"154\",\n    \"prompt\": \"A stunning selfie at sunset on the beach\",\n    \"prompt_enhancement\": true,\n    \"style\": \"glam\",\n    \"resolution\": \"9:16\"\n  }'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\nresponse = requests.post(\n    'https://api.oh.xyz/api/v1/images',\n    headers={'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY'},\n    json={\n        'character_id': '154',\n        'prompt': 'A stunning selfie at sunset on the beach',\n        'prompt_enhancement': True,\n        'style': 'glam',  # or 'amateur'; only read when prompt_enhancement is True\n        'resolution': '9:16'\n    }\n)\nresult = response.json()\njob_id = result['job_id']\npresigned_url = result['presigned_url']\nprint('Job ID:', job_id)\nprint('Download URL:', presigned_url)"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"properties": {
									"character_id": {
										"type": "string",
										"description": "Character identifier for generation"
									},
									"prompt": {
										"type": "string",
										"description": "Description of the image to generate"
									},
									"prompt_enhancement": {
										"type": "boolean",
										"default": false,
										"description": "When enabled, the system rewrites your prompt in the chosen style (see style) and, at content level 4, renders the picture around the sexual position detected from your prompt (see position). When disabled, your prompt is used as-is and style and position are ignored."
									},
									"style": {
										"type": "string",
										"enum": ["glam", "amateur"],
										"default": "glam",
										"description": "Look of the generated picture, applied at every content level (1–4) when prompt_enhancement is true. \"glam\" (default): a professional-looking photograph in even natural light, a clean high-end setting when your prompt names no place, natural unretouched skin. \"amateur\": a handheld consumer-camera snapshot lit by the camera flash, uneven lighting, slight tilt, dull washed-out colours, a messy bedroom when your prompt names no place. A place named in your prompt is honoured in both styles. Silently ignored when prompt_enhancement is false. Any other value returns 400. The job status reports results.style and results.style_applied. Ignored for a character created with style \"artificial\": with prompt_enhancement true such a character always renders in its own artificial look at every level and the job reports results.style \"artificial\" with style_applied true. \"artificial\" is a property of the character, not a value you can send here (400)."
									},
									"position": {
										"type": "string",
										"enum": [
											"blowjob",
											"blowjob_pov",
											"bukakke",
											"cowgirl",
											"cowgirl_pov",
											"cunnilingus",
											"deepthroat",
											"doggy",
											"doggy_pov",
											"double_penetration",
											"facesitting",
											"footjob",
											"gloryhole",
											"handjob",
											"handjob_pov",
											"masturbation_dildo",
											"masturbation_hand",
											"missionary",
											"missionary_pov",
											"mmf_threesome",
											"prone_bone",
											"reverse_cowgirl",
											"sixty_nine",
											"standing_from_behind",
											"strapon",
											"titjob",
											"tribbing"
										],
										"description": "Sexual position to render an explicit (level 4) request in; only read when prompt_enhancement is true and the request is detected as level 4. Omit it to have the position detected from your prompt; set it to fix the position regardless of the wording. Any other value returns 400. All 27 names are accepted, but a position that is not renderable yet fails the job with the position named in its error — omit position if the request must render regardless (auto-detection only picks renderable positions). Ignored everywhere else (levels 1–3, prompt_enhancement false, room_id jobs); the job status then reports results.position null."
									},
									"user_gender": {
										"type": "string",
										"enum": ["male", "female"],
										"description": "Gender of the user. Used to tailor the generated scene when prompt enhancement is enabled. If omitted, defaults to the opposite of the character's gender."
									},
									"resolution": {
										"oneOf": [
											{
												"type": "array",
												"items": {
													"type": "integer",
													"minimum": 100,
													"maximum": 2048
												},
												"minItems": 2,
												"maxItems": 2,
												"description": "Explicit [width, height] in pixels, each 100–2048"
											},
											{
												"type": "string",
												"enum": ["9:16", "16:9", "1:1", "4:3", "3:4"],
												"description": "Aspect ratio preset"
											}
										],
										"description": "Output resolution. Can be an aspect ratio string (\"9:16\", \"16:9\", \"1:1\", \"4:3\", \"3:4\") or an explicit [width, height] array. Aspect ratio presets map to: 9:16 → 720×1280, 16:9 → 1280×720, 1:1 → 1024×1024, 4:3 → 960×720, 3:4 → 720×960. A [width, height] array must hold exactly 2 whole numbers, each between 100 and 2048; any other value returns 400. On explicit (level 4) requests with prompt_enhancement true, each side is rounded to the nearest multiple of 16 (e.g. [1000, 1000] renders at 1008×1008), and when resolution is omitted the picture is 832×1280 portrait."
									},
									"room_id": {
										"type": "string",
										"description": "Room identifier for history-aware generation. In-room jobs use the standard history-aware enhancer: style and position do not apply (results.style_applied false, results.position null)."
									},
									"user": {
										"$ref": "#/components/schemas/User"
									}
								},
								"anyOf": [
									{
										"required": ["character_id", "prompt"]
									},
									{
										"required": ["room_id"]
									}
								]
							},
							"example": {
								"character_id": "154",
								"prompt": "A stunning selfie at sunset on the beach",
								"prompt_enhancement": true,
								"style": "glam",
								"resolution": "9:16"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Image generation job accepted",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AsyncGenerationResponse"
								},
								"example": {
									"message": "Image generation started",
									"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
									"status": "processing",
									"presigned_url": "https://s3.amazonaws.com/bucket/generated/image/154/2026-04/a1b2c3d4.png?X-Amz-..."
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/videos/create": {
			"post": {
				"tags": ["Videos"],
				"summary": "Generate a video asynchronously. Supports text-to-video, image-to-video, and in-room generation.",
				"description": "Use character_id for text-to-video, image_url for image-to-video, or room_id for history-aware generation.",
				"operationId": "generateVideo",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "// Image-to-Video: animate your own image with a specific position\nconst i2vResponse = await fetch('https://api.oh.xyz/api/v1/videos/create', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY' },\n  body: JSON.stringify({\n    imageUrl: 'https://example.com/my-image.jpg',\n    prompt: 'Riding motion with bouncing',\n    category: 'cowgirl',\n    videoPath: 'videos/my-customer/1234567890-5s.mp4',\n    videoLength: 5\n  })\n});\nconst i2vData = await i2vResponse.json();\n// Poll: GET /api/v1/videos/get?videoId={i2vData.id}\n\n// Text-to-Video: generate from character\nconst t2vResponse = await fetch('https://api.oh.xyz/api/v1/videos/create', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY' },\n  body: JSON.stringify({\n    character_id: '154',\n    prompt: 'Waving at the camera and smiling',\n    length: 5,\n    resolution: '9:16'\n  })\n});\nconst t2vData = await t2vResponse.json();\n// Poll: GET /api/v1/jobs/{t2vData.job_id}/status"
					},
					{
						"lang": "cURL",
						"source": "# Image-to-Video\ncurl -X POST 'https://api.oh.xyz/api/v1/videos/create' \\\n  -H 'Content-Type: application/json' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -d '{\n    \"imageUrl\": \"https://example.com/my-image.jpg\",\n    \"prompt\": \"Riding motion with bouncing\",\n    \"category\": \"cowgirl\",\n    \"videoPath\": \"videos/my-customer/1234567890-5s.mp4\",\n    \"videoLength\": 5\n  }'\n\n# Text-to-Video\ncurl -X POST 'https://api.oh.xyz/api/v1/videos/create' \\\n  -H 'Content-Type: application/json' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -d '{\n    \"character_id\": \"154\",\n    \"prompt\": \"Waving at the camera and smiling\",\n    \"length\": 5,\n    \"resolution\": \"9:16\"\n  }'"
					},
					{
						"lang": "Python",
						"source": "import requests\n\n# Image-to-Video\ni2v = requests.post(\n    'https://api.oh.xyz/api/v1/videos/create',\n    headers={'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY'},\n    json={\n        'imageUrl': 'https://example.com/my-image.jpg',\n        'prompt': 'Riding motion with bouncing',\n        'category': 'cowgirl',\n        'videoPath': 'videos/my-customer/1234567890-5s.mp4',\n        'videoLength': 5\n    }\n).json()\n# Poll: GET /api/v1/videos/get?videoId={i2v['id']}\n\n# Text-to-Video\nt2v = requests.post(\n    'https://api.oh.xyz/api/v1/videos/create',\n    headers={'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY'},\n    json={\n        'character_id': '154',\n        'prompt': 'Waving at the camera and smiling',\n        'length': 5,\n        'resolution': '9:16'\n    }\n).json()\n# Poll: GET /api/v1/jobs/{t2v['job_id']}/status"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["prompt"],
								"description": "Two modes are supported depending on which fields you provide. For image-to-video, provide `imageUrl`, `category`, and `videoPath`. For text-to-video, provide `character_id`.",
								"properties": {
									"prompt": {
										"type": "string",
										"minLength": 1,
										"description": "Description of the video content or motion to generate."
									},
									"imageUrl": {
										"type": "string",
										"format": "uri",
										"description": "**Image-to-Video mode.** URL of the source image to animate. Required for i2v mode."
									},
									"category": {
										"type": "string",
										"enum": [
											"blowjob",
											"pov_blowjob",
											"cowgirl",
											"pov_cowgirl",
											"reverse_cowgirl",
											"pov_reverse_cowgirl",
											"pov_missionary",
											"doggy",
											"pov_doggy",
											"cunnilingus",
											"handjob",
											"pov_handjob",
											"foot_job",
											"pov_foot_job",
											"tit_job",
											"standing_up_sex_from_behind"
										],
										"description": "**Image-to-Video mode.** Motion/position category for the video. Determines what kind of motion is applied to the source image. Required for i2v mode."
									},
									"videoPath": {
										"type": "string",
										"description": "**Image-to-Video mode.** S3 storage path for the output video (e.g., `videos/{customerId}/{timestamp}-5s.mp4`). Required for i2v mode."
									},
									"videoLength": {
										"type": "integer",
										"enum": [5, 10, 15],
										"description": "**Image-to-Video mode.** Video length in seconds. Must be 5, 10, or 15."
									},
									"character_id": {
										"type": "string",
										"description": "**Text-to-Video mode.** Character identifier. The system generates a starting image from the character and animates it."
									},
									"image_url": {
										"type": "string",
										"format": "uri",
										"description": "**Text-to-Video mode.** Alternative to character_id — provide your own source image. The system will animate it based on the prompt with automatic action detection."
									},
									"prompt_enhancement": {
										"type": "boolean",
										"default": true,
										"description": "**Text-to-Video mode.** When enabled, the system enhances your prompt using AI to improve video quality and motion. Enabled by default."
									},
									"resolution": {
										"oneOf": [
											{
												"type": "array",
												"items": {
													"type": "integer"
												},
												"minItems": 2,
												"maxItems": 2,
												"description": "Explicit [width, height] in pixels"
											},
											{
												"type": "string",
												"enum": ["9:16", "16:9", "1:1", "4:3", "3:4"],
												"description": "Aspect ratio preset"
											}
										],
										"description": "**Text-to-Video mode.** Output resolution. Can be an aspect ratio string or [width, height] array. Aspect ratio presets map to: 9:16 → 720×1800, 16:9 → 1280×720, 1:1 → 1024×1024, 4:3 → 960×720, 3:4 → 720×960."
									},
									"length": {
										"type": "integer",
										"enum": [5, 10, 15],
										"default": 5,
										"description": "**Text-to-Video mode.** Video length in seconds. Must be 5, 10, or 15."
									},
									"room_id": {
										"type": "string",
										"description": "Room identifier for history-aware generation."
									},
									"user": {
										"$ref": "#/components/schemas/User"
									}
								}
							},
							"examples": {
								"i2v": {
									"summary": "Image-to-Video: animate an image with a specific position",
									"value": {
										"imageUrl": "https://example.com/my-image.jpg",
										"prompt": "Riding motion with bouncing",
										"category": "cowgirl",
										"videoPath": "videos/my-customer/1234567890-5s.mp4",
										"videoLength": 5
									}
								},
								"t2v_character": {
									"summary": "Text-to-Video: generate from character",
									"value": {
										"character_id": "154",
										"prompt": "Waving at the camera and smiling",
										"length": 5,
										"resolution": "9:16"
									}
								},
								"t2v_image": {
									"summary": "Text-to-Video: animate your image with auto-detected action",
									"value": {
										"image_url": "https://example.com/my-image.jpg",
										"prompt": "Natural movement and motion",
										"length": 10
									}
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Video generation job accepted (Image-to-Video mode)",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"id": {
											"type": "string",
											"description": "Video job ID. Poll status with GET /api/v1/videos/get?videoId={id}"
										},
										"status": {
											"type": "string",
											"enum": ["GENERATING"],
											"description": "Initial job status"
										}
									}
								},
								"example": {
									"id": "abc123-def456",
									"status": "GENERATING"
								}
							}
						}
					},
					"202": {
						"description": "Video generation job accepted (Text-to-Video mode)",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"message": {
											"type": "string",
											"description": "Status message"
										},
										"job_id": {
											"type": "string",
											"description": "Job ID. Poll status with GET /api/v1/jobs/{job_id}/status"
										},
										"status": {
											"type": "string",
											"enum": ["processing"],
											"description": "Initial job status"
										},
										"presigned_url": {
											"type": "string",
											"description": "Presigned download URL for the video (valid for 7 days)"
										}
									}
								},
								"example": {
									"message": "Video generation started",
									"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
									"status": "processing",
									"presigned_url": "https://s3.amazonaws.com/..."
								}
							}
						}
					},
					"400": {
						"description": "Validation error or missing required fields",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"error": {
											"type": "string"
										}
									}
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/videos/t2v-pro": {
			"post": {
				"tags": ["Videos"],
				"summary": "Generate an explicit video of a character from a text prompt.",
				"description": "The first frame places the character in the scene your prompt describes, then it is animated with motion, sound and speech. The character must allow explicit (level 4) content and be female. Billed per second of requested length.",
				"operationId": "generateT2VProVideo",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "// Explicit video of a character (level 4, female) — async\nconst res = await fetch('https://api.oh.xyz/api/v1/videos/t2v-pro', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY' },\n  body: JSON.stringify({\n    character_id: '154',\n    prompt: 'deepthroat in the park at night',\n    length: 5\n  })\n});\nconst { job_id } = await res.json();\n// Poll: GET /api/v1/jobs/{job_id}/status — results.first_frame_url arrives\n// before the video, and url is set once status is 'completed'."
					},
					{
						"lang": "cURL",
						"source": "curl -X POST 'https://api.oh.xyz/api/v1/videos/t2v-pro' \\\n  -H 'Content-Type: application/json' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -d '{\n    \"character_id\": \"154\",\n    \"prompt\": \"deepthroat in the park at night\",\n    \"length\": 5\n  }'\n\n# Poll: GET /api/v1/jobs/{job_id}/status"
					},
					{
						"lang": "Python",
						"source": "import requests\n\njob = requests.post(\n    'https://api.oh.xyz/api/v1/videos/t2v-pro',\n    headers={'Content-Type': 'application/json', 'x-api-key': 'YOUR_API_KEY'},\n    json={\n        'character_id': '154',\n        'prompt': 'deepthroat in the park at night',\n        'length': 5\n    }\n).json()\n# Poll: GET /api/v1/jobs/{job['job_id']}/status"
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["prompt", "character_id"],
								"properties": {
									"prompt": {
										"type": "string",
										"minLength": 1,
										"maxLength": 2000,
										"description": "What should happen in the video, in any language."
									},
									"character_id": {
										"type": "string",
										"minLength": 1,
										"description": "Character to star in the video. Must be accessible to your account, allow explicit (level 4) content, and be female."
									},
									"length": {
										"type": "integer",
										"minimum": 2,
										"maximum": 30,
										"default": 5,
										"description": "Video length in whole seconds, 2 to 30. Billed per second."
									},
									"prompt_enhancement": {
										"type": "boolean",
										"default": true,
										"description": "Write a detailed video prompt from your request (motion, sounds, speech). Set false to send your prompt to the video model as written."
									},
									"style": {
										"type": "string",
										"enum": ["glam", "amateur"],
										"default": "glam",
										"description": "Look of the first frame."
									},
									"position": {
										"type": "string",
										"enum": [
											"blowjob",
											"blowjob_pov",
											"bukakke",
											"cowgirl",
											"cowgirl_pov",
											"cunnilingus",
											"deepthroat",
											"doggy",
											"doggy_pov",
											"double_penetration",
											"facesitting",
											"footjob",
											"gloryhole",
											"handjob",
											"handjob_pov",
											"masturbation_dildo",
											"masturbation_hand",
											"missionary",
											"missionary_pov",
											"mmf_threesome",
											"prone_bone",
											"reverse_cowgirl",
											"sixty_nine",
											"standing_from_behind",
											"strapon",
											"titjob",
											"tribbing"
										],
										"description": "Sex position to show. Omit to have it chosen from your prompt. A position that is not available yet fails the job."
									}
								}
							},
							"example": {
								"character_id": "154",
								"prompt": "deepthroat in the park at night",
								"length": 5
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Video generation job accepted.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"message": {
											"type": "string",
											"description": "Status message"
										},
										"job_id": {
											"type": "string",
											"description": "Job ID. Poll status with GET /api/v1/jobs/{job_id}/status"
										},
										"status": {
											"type": "string",
											"enum": ["processing"],
											"description": "Initial job status"
										},
										"presigned_url": {
											"type": "string",
											"description": "Presigned download URL for the video (valid for 7 days, usable once the job completes)"
										}
									}
								},
								"example": {
									"message": "Video generation started",
									"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
									"status": "processing",
									"presigned_url": "https://s3.amazonaws.com/..."
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or the character was not found, is male, or does not allow explicit content.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"403": {
						"description": "The character is not accessible to your account, or it is a digital twin awaiting approval.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/videos/get": {
			"get": {
				"tags": ["Videos"],
				"summary": "Get video status and URL.",
				"description": "Poll this endpoint to check the status of an image-to-video generation job. Use the `id` returned from the video creation response. Recommended polling interval: every 5 seconds, with a maximum timeout of 5 minutes. Once status is `READY`, the `video_url` field contains a presigned download URL.",
				"operationId": "getVideoStatus",
				"x-code-samples": [
					{
						"lang": "JavaScript",
						"source": "async function pollVideo(videoId, apiKey, maxAttempts = 60) {\n  for (let i = 0; i < maxAttempts; i++) {\n    const response = await fetch(\n      `https://api.oh.xyz/api/v1/videos/get?videoId=${videoId}`,\n      { headers: { 'x-api-key': apiKey } }\n    );\n    const data = await response.json();\n\n    if (data.status === 'READY') {\n      console.log('Video URL:', data.video_url);\n      return data;\n    }\n    if (data.status === 'FAILED') {\n      throw new Error('Video generation failed');\n    }\n\n    await new Promise(r => setTimeout(r, 5000));\n  }\n  throw new Error('Polling timed out');\n}"
					},
					{
						"lang": "cURL",
						"source": "curl -X GET 'https://api.oh.xyz/api/v1/videos/get?videoId=YOUR_VIDEO_ID' \\\n  -H 'x-api-key: YOUR_API_KEY'"
					},
					{
						"lang": "Python",
						"source": "import requests, time\n\ndef poll_video(video_id, api_key, max_attempts=60):\n    for _ in range(max_attempts):\n        data = requests.get(\n            f'https://api.oh.xyz/api/v1/videos/get?videoId={video_id}',\n            headers={'x-api-key': api_key}\n        ).json()\n\n        if data['status'] == 'READY':\n            print('Video URL:', data['video_url'])\n            return data\n        if data['status'] == 'FAILED':\n            raise Exception('Video generation failed')\n\n        time.sleep(5)\n    raise TimeoutError('Polling timed out')"
					}
				],
				"parameters": [
					{
						"name": "videoId",
						"in": "query",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The video ID returned from the image-to-video creation request"
					}
				],
				"responses": {
					"200": {
						"description": "Video status retrieved",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"id": {
											"type": "string",
											"description": "Video job ID"
										},
										"status": {
											"type": "string",
											"enum": ["GENERATING", "READY", "FAILED"],
											"description": "Current job status"
										},
										"video_url": {
											"type": "string",
											"nullable": true,
											"description": "Presigned download URL. Only present when status is READY."
										}
									}
								},
								"examples": {
									"generating": {
										"summary": "Video is still generating",
										"value": {
											"id": "abc123-def456",
											"status": "GENERATING"
										}
									},
									"ready": {
										"summary": "Video is ready",
										"value": {
											"id": "abc123-def456",
											"status": "READY",
											"video_url": "https://s3.amazonaws.com/bucket/videos/abc123/output.mp4?X-Amz-..."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Video not found"
					}
				}
			}
		},
		"/api/v1/videos/delete": {
			"delete": {
				"tags": ["Videos"],
				"summary": "Delete a video.",
				"description": "Delete a video by ID.",
				"operationId": "deleteVideo",
				"parameters": [
					{
						"name": "videoId",
						"in": "query",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The video ID to delete"
					}
				],
				"responses": {
					"200": {
						"description": "Video deleted successfully",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"ok": {
											"type": "boolean"
										},
										"status": {
											"type": "string"
										}
									}
								},
								"example": {
									"ok": true,
									"status": "deleted"
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"404": {
						"description": "Video not found"
					}
				}
			}
		},
		"/api/v1/profile-extraction/extract": {
			"post": {
				"tags": ["Profile Extraction"],
				"summary": "Extract a creator profile from an interview transcript.",
				"description": "Extract a creator profile from an interview transcript.",
				"operationId": "extractCreatorProfile",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["creatorId", "transcript"],
								"properties": {
									"creatorId": {
										"type": "string",
										"description": "Creator identifier."
									},
									"transcript": {
										"type": "array",
										"items": {
											"type": "object",
											"required": ["role", "message"],
											"properties": {
												"role": {
													"type": "string",
													"description": "Transcript speaker role."
												},
												"message": {
													"type": "string",
													"description": "Transcript message."
												},
												"time_in_call_secs": {
													"type": "number",
													"minimum": 0,
													"description": "Time in the call."
												}
											}
										}
									}
								}
							},
							"example": {
								"creatorId": "creator_123",
								"transcript": [
									{
										"role": "interviewer",
										"message": "Tell me about your style.",
										"time_in_call_secs": 12
									}
								]
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"additionalProperties": true,
									"description": "Extracted creator profile."
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/voices": {
			"get": {
				"tags": ["Voices"],
				"summary": "Get default voices.",
				"description": "Get default voices.",
				"operationId": "getDefaultVoices",
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"additionalProperties": true
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/voices/elevenlabs": {
			"get": {
				"tags": ["Voices"],
				"summary": "Get popular public ElevenLabs voices.",
				"description": "Get popular public ElevenLabs voices.",
				"operationId": "getElevenLabsVoices",
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"type": "object",
										"additionalProperties": true
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/face-similarity/compare": {
			"post": {
				"tags": ["Face Similarity"],
				"summary": "Compare two faces for 1:1 verification.",
				"description": "Compare two faces for 1:1 verification.",
				"operationId": "compareFaces",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["image_a_url", "image_b_url"],
								"properties": {
									"image_a_url": {
										"type": "string",
										"description": "First image URL.",
										"format": "uri"
									},
									"image_b_url": {
										"type": "string",
										"description": "Second image URL.",
										"format": "uri"
									},
									"threshold": {
										"type": "number",
										"format": "float",
										"minimum": 0,
										"maximum": 1
									},
									"request_id": {
										"type": "string",
										"description": "Optional caller-provided identifier."
									}
								}
							},
							"example": {
								"image_a_url": "https://cdn.example.com/face-a.jpg",
								"image_b_url": "https://cdn.example.com/face-b.jpg"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"match": {
											"type": "boolean"
										},
										"similarity": {
											"type": "number",
											"format": "float"
										},
										"request_id": {
											"type": "string",
											"description": "Caller-provided identifier."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		},
		"/api/v1/face-similarity/embed": {
			"post": {
				"tags": ["Face Similarity"],
				"summary": "Embed a single face.",
				"description": "Embed a single face.",
				"operationId": "embedFace",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": ["image_url"],
								"properties": {
									"image_url": {
										"type": "string",
										"description": "Image URL.",
										"format": "uri"
									},
									"request_id": {
										"type": "string",
										"description": "Optional caller-provided identifier."
									}
								}
							},
							"example": {
								"image_url": "https://cdn.example.com/face.jpg"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Request completed successfully.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"embedding": {
											"type": "array",
											"items": {
												"type": "number"
											}
										},
										"request_id": {
											"type": "string",
											"description": "Caller-provided identifier."
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Invalid or malformed request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"401": {
						"description": "Missing or invalid API key.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					},
					"500": {
						"description": "The request could not be completed because of a server error.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ApiError"
								}
							}
						}
					}
				}
			}
		}
	},
	"components": {
		"securitySchemes": {
			"ApiKeyAuth": {
				"type": "apiKey",
				"in": "header",
				"name": "X-API-Key",
				"description": "Partner API key. Keep API keys server-side and do not expose them in browser clients or mobile apps."
			}
		},
		"schemas": {
			"CharacterStatus": {
				"type": "string",
				"enum": ["draft", "generating", "ready", "saving", "saved", "failed"],
				"description": "V2 character processing status:\n- **draft**: Initial state\n- **generating**: AI generation in progress\n- **ready**: Generation complete, ready to save\n- **saving**: Save in progress\n- **saved**: Fully saved and ready to use\n- **failed**: Processing failed (check errorMessage)"
			},
			"GenerateCharacterRequest": {
				"type": "object",
				"required": [
					"nationality",
					"ethnicity",
					"firstName",
					"lastName",
					"gender"
				],
				"properties": {
					"nationality": {
						"type": "string",
						"description": "Character nationality. Use GET /api/v1/characters/nationalities for valid values",
						"example": "American"
					},
					"ethnicity": {
						"type": "string",
						"description": "Character ethnicity. Use GET /api/v1/characters/ethnicities for valid values",
						"example": "Caucasian"
					},
					"firstName": {
						"type": "string",
						"description": "Character first name",
						"example": "Aria"
					},
					"lastName": {
						"type": "string",
						"description": "Character last name",
						"example": "Storm"
					},
					"biography": {
						"type": "string",
						"description": "Character biography/bio text. Auto-generated if missing",
						"example": "A free-spirited artist with a passion for adventure"
					},
					"dateOfBirth": {
						"type": "string",
						"format": "date",
						"description": "Date of birth (ISO format). Must be 21+. Defaults to age 27 if not provided",
						"example": "1995-06-15"
					},
					"alias": {
						"type": "string",
						"description": "Character alias/nickname"
					},
					"job": {
						"type": "string",
						"description": "Character occupation"
					},
					"whereYouLive": {
						"type": "string",
						"description": "Location where character lives"
					},
					"gender": {
						"type": "string",
						"enum": ["Female", "Male"],
						"description": "Character gender"
					},
					"penisSize": {
						"type": "string",
						"description": "Penis size (male only)",
						"enum": ["Small", "Huge", "Enormous"]
					},
					"vaginaHair": {
						"type": "string",
						"description": "Pubic hair style (female only). Use GET /api/v1/characters/vagina-hair for valid values",
						"enum": [
							"Bald",
							"Designer",
							"Diamond shape",
							"French wax",
							"Full bush",
							"Heart shape",
							"Landing strip",
							"Lightning bolt",
							"Long",
							"Natural",
							"Neat triangle",
							"Partially shaved sides",
							"Shaved",
							"Small trimmed patch",
							"Smooth wax",
							"Thin landing strip",
							"Thin vertical strip",
							"Tiny Brazilian",
							"Trimmed",
							"Trimmed short",
							"V shaped",
							"Wild"
						]
					},
					"vaginaSize": {
						"type": "string",
						"description": "Vagina size (female only). Use GET /api/v1/characters/vagina-sizes for valid values",
						"enum": [
							"Accommodating",
							"Average",
							"Extremely tight",
							"Gilf",
							"Gripping",
							"Incredibly tight",
							"Loose & wet",
							"Milf",
							"Normal",
							"Perfectly snug",
							"Petite tight",
							"Post-baby looser",
							"Relaxed",
							"Slightly looser",
							"Snug",
							"Super tight",
							"Tight",
							"Very Accommodating",
							"Very tight",
							"Well-used",
							"Youthfully tight"
						]
					},
					"physicalCharacteristics": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Array of additional physical features not covered by other fields (e.g., glasses, freckles, pregnancy, piercings)",
						"example": ["wears glasses", "has freckles", "beauty mark on cheek"]
					},
					"orientation": {
						"type": "string",
						"description": "Character's sexual orientation. Affects who the character is attracted to in text conversations.",
						"enum": [
							"90/10",
							"Asexual",
							"Bi-curious",
							"Bicurious",
							"Bisexual",
							"Demisexual",
							"Experimenting",
							"Fluid",
							"Gay",
							"Heteroflexible",
							"Homoflexible",
							"Lesbian",
							"Mostly straight",
							"Open to anything",
							"Pansexual",
							"Queer",
							"Sapiosexual",
							"Straight",
							"Straight but plays with girls"
						]
					},
					"personality": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Character's personality archetype. Shapes the character's overall behavior and tone in text conversations.",
						"example": ["Adventurous", "Playful", "Confident"]
					},
					"interests": {
						"type": "array",
						"description": "Character interests",
						"items": {
							"type": "string",
							"enum": [
								"Dancing",
								"Yoga",
								"Work",
								"Gaming",
								"Streaming",
								"Cosplay",
								"Anime",
								"Nature",
								"Animals",
								"Hiking",
								"Spirituality",
								"Exercise",
								"Reading",
								"Travelling",
								"Chess",
								"Tattoos",
								"Art",
								"Drawing",
								"Music",
								"Gym",
								"Running",
								"Tanning",
								"Dining out",
								"Cooking",
								"Partying",
								"Sports",
								"Magic",
								"Museums",
								"History",
								"Writing",
								"Watching movies",
								"Golf",
								"Tennis",
								"Photography",
								"Astrology",
								"Nightlife",
								"Adventure",
								"Fashion",
								"Soccer",
								"Theatre",
								"Cycling",
								"Exercising",
								"Business",
								"Clean eating",
								"The outdoors",
								"Baking",
								"Piano",
								"Charity",
								"Wine",
								"Restaurants",
								"Tech",
								"Psychology",
								"Meditation",
								"Chatting",
								"Gardening",
								"Painting",
								"Swimming",
								"Fitness",
								"Volleyball",
								"Movies",
								"Skateboarding",
								"Farming",
								"Climbing",
								"Camping",
								"Sailing",
								"Scuba diving",
								"Weightlifting",
								"Gymnastics",
								"Mixed martial arts",
								"Pole dancing",
								"Wine tasting",
								"Mountaineering",
								"Rowing",
								"Hockey",
								"Judo",
								"Manga",
								"Beach activities",
								"Digital art",
								"Football",
								"Motorcycles",
								"DIY",
								"Science",
								"Technology",
								"Shooting",
								"Driving",
								"Exploring",
								"Boxing",
								"Comedy",
								"Cars",
								"Rock music",
								"Jazz",
								"Shopping",
								"Health",
								"Stocks",
								"Films",
								"Basketball",
								"Dance",
								"Dogs",
								"Creativity",
								"Journalism",
								"Clubbing",
								"Raves",
								"Cheerleading",
								"Filmmaking",
								"Beach",
								"Travel",
								"Netflix",
								"Video games",
								"Home design",
								"Social media",
								"Acting",
								"Horses",
								"Beauty",
								"True crime"
							]
						}
					},
					"kinks": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Character's sexual preferences and kinks. Used to shape the character's personality and behavior in text conversations only — they do not control image or video generation capabilities."
					},
					"hairLength": {
						"type": "string",
						"description": "Hair length. Use GET /api/v1/characters/hair-lengths for valid values",
						"example": "Long"
					},
					"hairColour": {
						"type": "string",
						"description": "Hair colour. Use GET /api/v1/characters/hair-colours for valid values",
						"example": "Blonde"
					},
					"eyeColour": {
						"type": "string",
						"description": "Eye colour. Use GET /api/v1/characters/eye-colours for valid values",
						"example": "Blue"
					},
					"breastSize": {
						"type": "string",
						"description": "Breast size (female only). Use GET /api/v1/characters/breast-sizes for valid values. Auto-generated if missing",
						"example": "Medium"
					},
					"bodyBuild": {
						"type": "string",
						"description": "Body build type. Use GET /api/v1/characters/body-builds for valid values",
						"example": "Athletic"
					},
					"backstory": {
						"type": "string",
						"description": "Character backstory. Auto-generated if missing"
					},
					"textingStyle": {
						"type": "string",
						"description": "texting/communication style description. Auto-generated if missing",
						"example": "Playful and flirty with lots of emojis"
					},
					"tattoos": {
						"type": "string",
						"description": "tattoo description. Auto-generated if missing",
						"example": "Small butterfly on right shoulder"
					},
					"profileImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to profile image (downloaded and saved automatically)"
					},
					"coverVideoUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to cover video (downloaded and saved automatically)"
					},
					"voiceUrl": {
						"type": "string",
						"format": "uri",
						"description": "HTTP URL to voice audio file (downloaded automatically)"
					},
					"audioSpeed": {
						"type": "number",
						"format": "float",
						"minimum": 0.5,
						"maximum": 2,
						"default": 0.5,
						"description": "Audio playback speed multiplier"
					},
					"socialHandleInstagram": {
						"type": "string",
						"description": "Instagram handle"
					},
					"socialHandleTiktok": {
						"type": "string",
						"description": "TikTok handle"
					},
					"socialHandleTwitter": {
						"type": "string",
						"description": "X/Twitter handle"
					},
					"height": {
						"type": "string",
						"description": "Character height",
						"example": "5'7\""
					},
					"bodyShape": {
						"type": "string",
						"description": "Body shape. Use GET /api/v1/characters/body-shapes for valid values",
						"enum": [
							"Apple",
							"Athletic",
							"Balanced",
							"Bottom-heavy pear",
							"Busty rectangle",
							"Chubby",
							"Hourglass",
							"Inverted triangle",
							"Pear",
							"Petite hourglass",
							"Rectangle",
							"Slim hourglass",
							"Soft pear",
							"Thick hourglass",
							"Top-heavy",
							"Voluptuous hourglass"
						]
					},
					"buttSize": {
						"type": "string",
						"description": "Butt size. Use GET /api/v1/characters/butt-sizes for valid values",
						"enum": [
							"Average",
							"Big",
							"Bubble Butt",
							"Firm & round",
							"Flat",
							"Heart-shaped",
							"Huge",
							"Jiggly",
							"Juicy",
							"Massive",
							"Peach",
							"Perky",
							"Phat",
							"Round & Full",
							"Shelf",
							"Small",
							"Thick",
							"Wide"
						]
					},
					"skinTone": {
						"type": "string",
						"description": "Skin tone. Use GET /api/v1/characters/skin-tones for valid values",
						"enum": [
							"Alabaster",
							"Beige",
							"Caramel",
							"Deep Brown",
							"Deep Tan",
							"Ebony",
							"Espresso",
							"Golden",
							"Golden Olive",
							"Honey",
							"Latte",
							"Light",
							"Light Caramel",
							"Light Olive",
							"Mahogany",
							"Medium Brown",
							"Mocha",
							"Olive",
							"Pale Ivory",
							"Rich Brown",
							"Sun-Kissed",
							"Tan",
							"Tawny",
							"Very Pale",
							"Warm Honey",
							"Warm Ivory"
						]
					},
					"breastPertness": {
						"type": "string",
						"description": "Breast pertness (female only). Use GET /api/v1/characters/breast-pertness for valid values",
						"example": "Perky"
					},
					"nippleColour": {
						"type": "string",
						"description": "Nipple colour (female only). Use GET /api/v1/characters/nipple-colours for valid values",
						"enum": [
							"Almost black",
							"Caramel brown",
							"Dark brown",
							"Dark pink",
							"Deep brown",
							"Dusky rose",
							"Light brown",
							"Light pink",
							"Mauve",
							"Medium brown",
							"Medium pink",
							"Peachy pink",
							"Pink",
							"Puffy dark pink",
							"Puffy light pink",
							"Reddish-pink",
							"Rosy brown",
							"Tan brown"
						]
					},
					"relationshipStatus": {
						"type": "string",
						"description": "Relationship status"
					},
					"imageModel": {
						"type": "string",
						"description": "Image model for generation",
						"default": "flux"
					},
					"contentLevel": {
						"type": "string",
						"description": "Maximum content level the character will engage with. Determines what the character is willing to do in both text conversations and media generation. Sexy = suggestive/clothed, Topless = partial nudity, Nudes = full nudity, Sex = explicit sexual content.",
						"enum": ["Sexy", "Topless", "Nudes", "Sex"],
						"example": "Sex"
					},
					"bodyType": {
						"type": "string",
						"description": "Body type (valid value from lookup endpoint)"
					},
					"eyeColor": {
						"type": "string",
						"description": "Eye color (valid value from lookup endpoint)"
					},
					"hairColor": {
						"type": "string",
						"description": "Hair color (valid value from lookup endpoint)"
					},
					"socialHandleYoutube": {
						"type": "string",
						"description": "YouTube handle"
					},
					"socialHandleTwitch": {
						"type": "string",
						"description": "Twitch handle"
					},
					"socialHandleTelegram": {
						"type": "string",
						"description": "Telegram handle"
					},
					"earlyAccessEmail": {
						"type": "integer",
						"description": "Early access email entity reference"
					},
					"weight": {
						"type": "integer",
						"description": "Character weight/ordering",
						"default": 200
					},
					"priorityPrice": {
						"type": "number",
						"format": "float",
						"default": 9.99,
						"description": "Priority subscription price (field_sub_a)"
					},
					"platinumPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Platinum subscription price (field_sub_b)"
					},
					"vipPrice": {
						"type": "number",
						"format": "float",
						"default": 39.99,
						"description": "VIP subscription price (field_sub_c)"
					},
					"priorityListPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Priority list price (field_list_price_a)"
					},
					"sexDrive": {
						"type": "string",
						"description": "Character's sex drive level. Affects the character's eagerness and initiative in text conversations.",
						"enum": [
							"Always horny",
							"Asexual but enjoys pleasing",
							"Average",
							"High",
							"High but shy",
							"Hypersexual nympho",
							"Insatiable",
							"Low",
							"Low until aroused then wild",
							"Moderate",
							"Morning sex addict",
							"Needs it to fall asleep",
							"Night owl horny",
							"Once a day minimum",
							"Only horny when ovulating",
							"Permanently in heat",
							"Switches normal to crazy",
							"Touch-starved",
							"Very high",
							"Very low cuddly"
						]
					},
					"conversationStyle": {
						"type": "string",
						"description": "Character's conversation style. Controls the tone and manner of the character's text responses.",
						"enum": [
							"Baby-talk / little space",
							"Bratty & sassy",
							"Bubbly & giggly",
							"Constant compliments & praise",
							"Country / southern drawl",
							"Dirty talk queen",
							"Dominant & commanding",
							"Flirty & teasing",
							"Foul-mouthed & trashy",
							"High-maintenance princess",
							"Low-key & chill",
							"Nerdy & rambling",
							"Proper & polite",
							"Sarcastic & witty",
							"Shy & soft-spoken",
							"Submissive & obedient",
							"Sweet & innocent",
							"Tsundere",
							"Valley girl",
							"Weeb / anime speech"
						]
					},
					"sexualExperience": {
						"type": "string",
						"description": "Character's sexual experience level. Affects the character's personality and how they discuss sexual topics in text conversations.",
						"enum": [
							"100+ partners",
							"Almost virgin",
							"Always slutty",
							"Experienced",
							"Former escort",
							"Highly experienced",
							"Innocent but curious",
							"Lots of threesomes",
							"Moderate",
							"Mostly one-night stands",
							"Nymphomaniac",
							"Only relationships",
							"Porn-level",
							"Recently awakened",
							"Reformed good girl",
							"Sheltered then wild",
							"Swinger",
							"Very experienced",
							"Very limited",
							"Virgin"
						]
					},
					"additionalDescription": {
						"type": "string",
						"maxLength": 1000,
						"description": "Free-text traits added to the reference image that the structured fields can't express. Examples: `nose piercing`, `hooped earrings`, `cherry blossom sleeve tattoo`, `wire-frame glasses`, `thin scar above the left eyebrow`.",
						"example": "six months pregnant, small dragon tattoo across the right hip, wire-frame glasses, a thin scar above the left eyebrow"
					},
					"style": {
						"type": "string",
						"enum": ["realistic", "artificial"],
						"default": "realistic",
						"description": "Visual style of the generated reference image: realistic (photorealistic, default) or artificial (glossy semi-real AI-influencer look: a deliberately AI-looking, hyper-polished character, vivid, glossy and larger than life rather than photorealistic). The choice is stored with the character: GET /api/v2/characters/{characterGuid} echoes it as character.style, and every image job for the character follows it (see \"Image styles and explicit-level rendering\" under Images). Any other value returns 400.",
						"example": "realistic"
					},
					"attitude": {
						"type": "string",
						"description": "Character attitude."
					},
					"relationshipStyle": {
						"type": "string",
						"description": "Preferred relationship style."
					},
					"kinksStrings": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Free-form kink descriptions."
					},
					"extraDetails": {
						"type": "string",
						"description": "Additional profile and preference details."
					},
					"sexPositions": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Preferred sex positions."
					},
					"roleplayPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"flirtingPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"imageRequestPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					}
				}
			},
			"SaveCharacterRequest": {
				"type": "object",
				"required": ["characterGuid"],
				"properties": {
					"nationality": {
						"type": "string",
						"description": "Character nationality. Use GET /api/v1/characters/nationalities for valid values",
						"example": "American"
					},
					"ethnicity": {
						"type": "string",
						"description": "Override ethnicity"
					},
					"firstName": {
						"type": "string",
						"description": "Override first name"
					},
					"lastName": {
						"type": "string",
						"description": "Override last name"
					},
					"biography": {
						"type": "string",
						"description": "Override biography"
					},
					"dateOfBirth": {
						"type": "string",
						"format": "date",
						"description": "Override date of birth (must be 21+)"
					},
					"alias": {
						"type": "string",
						"description": "Override alias/nickname"
					},
					"job": {
						"type": "string",
						"description": "Override job title"
					},
					"whereYouLive": {
						"type": "string",
						"description": "Override location"
					},
					"gender": {
						"type": "string",
						"description": "Override gender"
					},
					"penisSize": {
						"type": "string",
						"description": "Override penis size (male only)"
					},
					"vaginaHair": {
						"type": "string",
						"description": "Pubic hair style (female only). Use GET /api/v1/characters/vagina-hair for valid values",
						"enum": [
							"Bald",
							"Designer",
							"Diamond shape",
							"French wax",
							"Full bush",
							"Heart shape",
							"Landing strip",
							"Lightning bolt",
							"Long",
							"Natural",
							"Neat triangle",
							"Partially shaved sides",
							"Shaved",
							"Small trimmed patch",
							"Smooth wax",
							"Thin landing strip",
							"Thin vertical strip",
							"Tiny Brazilian",
							"Trimmed",
							"Trimmed short",
							"V shaped",
							"Wild"
						]
					},
					"vaginaSize": {
						"type": "string",
						"description": "Vagina size (female only). Use GET /api/v1/characters/vagina-sizes for valid values",
						"enum": [
							"Accommodating",
							"Average",
							"Extremely tight",
							"Gilf",
							"Gripping",
							"Incredibly tight",
							"Loose & wet",
							"Milf",
							"Normal",
							"Perfectly snug",
							"Petite tight",
							"Post-baby looser",
							"Relaxed",
							"Slightly looser",
							"Snug",
							"Super tight",
							"Tight",
							"Very Accommodating",
							"Very tight",
							"Well-used",
							"Youthfully tight"
						]
					},
					"physicalCharacteristics": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Override physical characteristics (array of strings)",
						"example": ["wears glasses", "has freckles"]
					},
					"orientation": {
						"type": "string",
						"description": "Override sexual orientation"
					},
					"personality": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Override personality traits. Values must match valid terms — use GET /api/v1/characters/personalities for the full list"
					},
					"interests": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Override interests. Values must match valid terms — use GET /api/v1/characters/interests for the full list"
					},
					"kinks": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Character's sexual preferences and kinks. Used to shape the character's personality and behavior in text conversations only — they do not control image or video generation capabilities."
					},
					"hairLength": {
						"type": "string",
						"description": "Override hair length"
					},
					"hairColour": {
						"type": "string",
						"description": "Hair colour. Use GET /api/v1/characters/hair-colours for valid values",
						"example": "Blonde"
					},
					"eyeColour": {
						"type": "string",
						"description": "Eye colour. Use GET /api/v1/characters/eye-colours for valid values",
						"example": "Blue"
					},
					"breastSize": {
						"type": "string",
						"description": "Override breast size (female only)"
					},
					"bodyBuild": {
						"type": "string",
						"description": "Body build type. Use GET /api/v1/characters/body-builds for valid values",
						"example": "Athletic"
					},
					"backstory": {
						"type": "string",
						"description": "Override backstory"
					},
					"textingStyle": {
						"type": "string",
						"description": "Override texting/communication style",
						"example": "Playful and flirty with lots of emojis"
					},
					"tattoos": {
						"type": "string",
						"description": "Override tattoo description",
						"example": "Small butterfly on right shoulder"
					},
					"profileImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to profile image (downloaded, cropped to 1:1, saved to S3)"
					},
					"coverVideoUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to cover video (downloaded and saved to S3)"
					},
					"voiceUrl": {
						"type": "string",
						"format": "uri",
						"description": "HTTP URL to voice reference audio file (.wav). Overrides any voice set at generate time"
					},
					"audioSpeed": {
						"type": "number",
						"format": "float",
						"minimum": 0.5,
						"maximum": 2,
						"default": 0.5,
						"description": "Audio playback speed multiplier"
					},
					"socialHandleInstagram": {
						"type": "string",
						"description": "Instagram handle"
					},
					"socialHandleTiktok": {
						"type": "string",
						"description": "TikTok handle"
					},
					"socialHandleTwitter": {
						"type": "string",
						"description": "X/Twitter handle"
					},
					"height": {
						"type": "string",
						"description": "Character height",
						"example": "5'7\""
					},
					"bodyShape": {
						"type": "string",
						"description": "Body shape. Use GET /api/v1/characters/body-shapes for valid values",
						"enum": [
							"Apple",
							"Athletic",
							"Balanced",
							"Bottom-heavy pear",
							"Busty rectangle",
							"Chubby",
							"Hourglass",
							"Inverted triangle",
							"Pear",
							"Petite hourglass",
							"Rectangle",
							"Slim hourglass",
							"Soft pear",
							"Thick hourglass",
							"Top-heavy",
							"Voluptuous hourglass"
						]
					},
					"buttSize": {
						"type": "string",
						"description": "Butt size. Use GET /api/v1/characters/butt-sizes for valid values",
						"enum": [
							"Average",
							"Big",
							"Bubble Butt",
							"Firm & round",
							"Flat",
							"Heart-shaped",
							"Huge",
							"Jiggly",
							"Juicy",
							"Massive",
							"Peach",
							"Perky",
							"Phat",
							"Round & Full",
							"Shelf",
							"Small",
							"Thick",
							"Wide"
						]
					},
					"skinTone": {
						"type": "string",
						"description": "Skin tone. Use GET /api/v1/characters/skin-tones for valid values",
						"enum": [
							"Alabaster",
							"Beige",
							"Caramel",
							"Deep Brown",
							"Deep Tan",
							"Ebony",
							"Espresso",
							"Golden",
							"Golden Olive",
							"Honey",
							"Latte",
							"Light",
							"Light Caramel",
							"Light Olive",
							"Mahogany",
							"Medium Brown",
							"Mocha",
							"Olive",
							"Pale Ivory",
							"Rich Brown",
							"Sun-Kissed",
							"Tan",
							"Tawny",
							"Very Pale",
							"Warm Honey",
							"Warm Ivory"
						]
					},
					"breastPertness": {
						"type": "string",
						"description": "Breast pertness (female only). Use GET /api/v1/characters/breast-pertness for valid values",
						"example": "Perky"
					},
					"nippleColour": {
						"type": "string",
						"description": "Nipple colour (female only). Use GET /api/v1/characters/nipple-colours for valid values",
						"enum": [
							"Almost black",
							"Caramel brown",
							"Dark brown",
							"Dark pink",
							"Deep brown",
							"Dusky rose",
							"Light brown",
							"Light pink",
							"Mauve",
							"Medium brown",
							"Medium pink",
							"Peachy pink",
							"Pink",
							"Puffy dark pink",
							"Puffy light pink",
							"Reddish-pink",
							"Rosy brown",
							"Tan brown"
						]
					},
					"relationshipStatus": {
						"type": "string",
						"description": "Override relationship status"
					},
					"imageModel": {
						"type": "string",
						"description": "Image model for generation",
						"default": "flux"
					},
					"contentLevel": {
						"allOf": [
							{
								"$ref": "#/components/schemas/GenerateCharacterRequest/properties/contentLevel"
							}
						],
						"description": "Maximum content level the character will engage with. Determines what the character is willing to do in both text conversations and media generation. Sexy = suggestive/clothed, Topless = partial nudity, Nudes = full nudity, Sex = explicit sexual content."
					},
					"bodyType": {
						"type": "string",
						"description": "Override body type (valid value from lookup endpoint)"
					},
					"eyeColor": {
						"type": "string",
						"description": "Override eye color (valid value from lookup endpoint)"
					},
					"hairColor": {
						"type": "string",
						"description": "Override hair color (valid value from lookup endpoint)"
					},
					"socialHandleYoutube": {
						"type": "string",
						"description": "YouTube handle"
					},
					"socialHandleTwitch": {
						"type": "string",
						"description": "Twitch handle"
					},
					"socialHandleTelegram": {
						"type": "string",
						"description": "Telegram handle"
					},
					"earlyAccessEmail": {
						"type": "integer",
						"description": "Early access email entity reference"
					},
					"weight": {
						"type": "integer",
						"description": "Character weight/ordering",
						"default": 200
					},
					"priorityPrice": {
						"type": "number",
						"format": "float",
						"default": 9.99,
						"description": "Override priority subscription price (field_sub_a)"
					},
					"platinumPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Override platinum subscription price (field_sub_b)"
					},
					"vipPrice": {
						"type": "number",
						"format": "float",
						"default": 39.99,
						"description": "Override vIP subscription price (field_sub_c)"
					},
					"priorityListPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Override priority list price (field_list_price_a)"
					},
					"sexDrive": {
						"type": "string",
						"description": "Character's sex drive level. Affects the character's eagerness and initiative in text conversations.",
						"enum": [
							"Always horny",
							"Asexual but enjoys pleasing",
							"Average",
							"High",
							"High but shy",
							"Hypersexual nympho",
							"Insatiable",
							"Low",
							"Low until aroused then wild",
							"Moderate",
							"Morning sex addict",
							"Needs it to fall asleep",
							"Night owl horny",
							"Once a day minimum",
							"Only horny when ovulating",
							"Permanently in heat",
							"Switches normal to crazy",
							"Touch-starved",
							"Very high",
							"Very low cuddly"
						]
					},
					"conversationStyle": {
						"type": "string",
						"description": "Character's conversation style. Controls the tone and manner of the character's text responses.",
						"enum": [
							"Baby-talk / little space",
							"Bratty & sassy",
							"Bubbly & giggly",
							"Constant compliments & praise",
							"Country / southern drawl",
							"Dirty talk queen",
							"Dominant & commanding",
							"Flirty & teasing",
							"Foul-mouthed & trashy",
							"High-maintenance princess",
							"Low-key & chill",
							"Nerdy & rambling",
							"Proper & polite",
							"Sarcastic & witty",
							"Shy & soft-spoken",
							"Submissive & obedient",
							"Sweet & innocent",
							"Tsundere",
							"Valley girl",
							"Weeb / anime speech"
						]
					},
					"sexualExperience": {
						"type": "string",
						"description": "Character's sexual experience level. Affects the character's personality and how they discuss sexual topics in text conversations.",
						"enum": [
							"100+ partners",
							"Almost virgin",
							"Always slutty",
							"Experienced",
							"Former escort",
							"Highly experienced",
							"Innocent but curious",
							"Lots of threesomes",
							"Moderate",
							"Mostly one-night stands",
							"Nymphomaniac",
							"Only relationships",
							"Porn-level",
							"Recently awakened",
							"Reformed good girl",
							"Sheltered then wild",
							"Swinger",
							"Very experienced",
							"Very limited",
							"Virgin"
						]
					},
					"additionalDescription": {
						"type": "string",
						"maxLength": 1000,
						"description": "Free-text traits added to the reference image that the structured fields can't express. Examples: `nose piercing`, `hooped earrings`, `cherry blossom sleeve tattoo`, `wire-frame glasses`, `thin scar above the left eyebrow`.",
						"example": "six months pregnant, small dragon tattoo across the right hip, wire-frame glasses, a thin scar above the left eyebrow"
					},
					"attitude": {
						"type": "string",
						"description": "Character attitude."
					},
					"relationshipStyle": {
						"type": "string",
						"description": "Preferred relationship style."
					},
					"kinksStrings": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Free-form kink descriptions."
					},
					"extraDetails": {
						"type": "string",
						"description": "Additional profile and preference details."
					},
					"sexPositions": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Preferred sex positions."
					},
					"roleplayPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"flirtingPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"imageRequestPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"characterGuid": {
						"type": "string",
						"format": "uuid",
						"description": "Character GUID from the generate step"
					},
					"uid": {
						"type": "integer",
						"description": "User ID in OhChat system"
					},
					"platinumListPrice": {
						"type": "number",
						"format": "float",
						"default": 29.99,
						"description": "Override platinum list price (field_list_price_b)"
					}
				}
			},
			"DigitalTwinStatus": {
				"type": "string",
				"enum": ["pending", "active", "draft", "inactive"],
				"description": "Digital twin processing status:\n- **pending**: Creation/update in progress\n- **active**: Digital twin is ready and live\n- **draft**: Initial state before processing\n- **inactive**: Deactivated"
			},
			"CreateDigitalTwinRequest": {
				"type": "object",
				"required": [
					"name",
					"alias",
					"dateOfBirth",
					"job",
					"location",
					"gender",
					"orientation",
					"hairColour",
					"eyeColour",
					"bodyType",
					"referenceImageUrl",
					"contentLevel"
				],
				"properties": {
					"name": {
						"type": "string",
						"description": "Character display name",
						"example": "Luna Starfire"
					},
					"alias": {
						"type": "string",
						"description": "Character nickname / short name",
						"example": "Luna"
					},
					"dateOfBirth": {
						"type": "string",
						"format": "date",
						"description": "Date of birth (ISO format). Character must be 21+",
						"example": "1998-06-15"
					},
					"job": {
						"type": "string",
						"description": "Character occupation",
						"example": "Digital Artist"
					},
					"location": {
						"type": "string",
						"description": "Where the character lives",
						"example": "Los Angeles, CA"
					},
					"gender": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/gender"
					},
					"orientation": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/orientation"
					},
					"hairColour": {
						"type": "string",
						"description": "Hair colour. Use GET /api/v1/characters/hair-colours for valid values",
						"example": "Blonde"
					},
					"eyeColour": {
						"type": "string",
						"description": "Eye colour. Use GET /api/v1/characters/eye-colours for valid values",
						"example": "Blue"
					},
					"bodyType": {
						"type": "string",
						"description": "Body type. Use GET /api/v1/characters/body-builds for valid values",
						"example": "Athletic"
					},
					"referenceImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to reference image (downloaded and saved to S3)",
						"example": "https://cdn.example.com/images/reference.jpg"
					},
					"contentLevel": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/contentLevel"
					},
					"profileImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to profile image (converted to PNG and saved)",
						"example": "https://cdn.example.com/images/profile.jpg"
					},
					"bio": {
						"type": "string",
						"description": "Character biography"
					},
					"textingStyle": {
						"type": "string",
						"description": "Texting/communication style",
						"example": "Playful and flirty with lots of emojis"
					},
					"ethnicity": {
						"type": "string",
						"description": "Character ethnicity. Use GET /api/v1/characters/ethnicities for valid values",
						"example": "Caucasian"
					},
					"hairLength": {
						"type": "string",
						"description": "Hair length. Use GET /api/v1/characters/hair-lengths for valid values",
						"example": "Long"
					},
					"breastSize": {
						"type": "string",
						"description": "Breast size (female only). Use GET /api/v1/characters/breast-sizes for valid values",
						"example": "Medium"
					},
					"tattoos": {
						"type": "string",
						"description": "Tattoo description",
						"example": "Small butterfly on shoulder"
					},
					"physicalCharacteristics": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Physical characteristics (array of strings)",
						"example": ["Freckles", "Dimples", "Beauty mark"]
					},
					"personality": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Personality traits. Values must match valid terms — use GET /api/v1/characters/personalities for the full list",
						"example": ["Playful", "Creative", "Confident"]
					},
					"interests": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Character interests. Values must match valid terms — use GET /api/v1/characters/interests for the full list",
						"example": ["Art", "Gaming", "Music"]
					},
					"kinks": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Character kinks. Values must match valid terms — use GET /api/v1/characters/kinks for the full list"
					},
					"audioSpeed": {
						"type": "number",
						"format": "float",
						"minimum": 0.5,
						"maximum": 2,
						"default": 1,
						"description": "Audio playback speed multiplier"
					},
					"socialHandleInstagram": {
						"type": "string",
						"description": "Instagram handle",
						"example": "@luna_starfire"
					},
					"vaginaHair": {
						"type": "string",
						"description": "Pubic hair style (female only). Use GET /api/v1/characters/vagina-hair for valid values",
						"example": "Landing strip"
					},
					"relationshipStatus": {
						"type": "string",
						"description": "Relationship status"
					},
					"backstory": {
						"type": "string",
						"description": "Character backstory. Auto-generated if not provided"
					},
					"coverVideoUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to cover video (downloaded and saved to S3)"
					},
					"voiceUrl": {
						"type": "string",
						"format": "uri",
						"description": "HTTP URL to voice reference audio file (.wav). For best results, ~30 seconds of clear speech",
						"example": "https://example.com/voice.wav"
					},
					"socialHandleTiktok": {
						"type": "string",
						"description": "TikTok handle"
					},
					"socialHandleTwitter": {
						"type": "string",
						"description": "X/Twitter handle"
					},
					"socialHandleYoutube": {
						"type": "string",
						"description": "YouTube handle"
					},
					"socialHandleTwitch": {
						"type": "string",
						"description": "Twitch handle"
					},
					"socialHandleTelegram": {
						"type": "string",
						"description": "Telegram handle"
					},
					"earlyAccessEmail": {
						"type": "integer",
						"description": "Early access email entity reference"
					},
					"priorityPrice": {
						"type": "number",
						"format": "float",
						"default": 9.99,
						"description": "Priority subscription price (field_sub_a)"
					},
					"platinumPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Platinum subscription price (field_sub_b)"
					},
					"vipPrice": {
						"type": "number",
						"format": "float",
						"default": 39.99,
						"description": "VIP subscription price (field_sub_c)"
					},
					"priorityListPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Priority list price (field_list_price_a)"
					},
					"platinumListPrice": {
						"type": "number",
						"format": "float",
						"default": 29.99,
						"description": "Platinum list price (field_list_price_b)"
					},
					"extraDetails": {
						"type": "string",
						"description": "Additional profile and preference details."
					},
					"roleplayPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"flirtingPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"imageRequestPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"profileImagePrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/ProfileImagePromptItem"
						}
					}
				}
			},
			"UpdateDigitalTwinRequest": {
				"type": "object",
				"description": "All fields are optional for partial updates. Reference image and ownership cannot be changed after creation.",
				"properties": {
					"name": {
						"type": "string",
						"description": "Character display name"
					},
					"alias": {
						"type": "string",
						"description": "Character nickname"
					},
					"dateOfBirth": {
						"type": "string",
						"format": "date",
						"description": "Date of birth (must remain 21+)"
					},
					"job": {
						"type": "string",
						"description": "Character occupation"
					},
					"location": {
						"type": "string",
						"description": "Where the character lives"
					},
					"bio": {
						"type": "string",
						"description": "Character biography"
					},
					"textingStyle": {
						"type": "string",
						"description": "Texting style"
					},
					"personality": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Update personality traits. Values must match valid terms — use GET /api/v1/characters/personalities for the full list"
					},
					"interests": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Update interests. Values must match valid terms — use GET /api/v1/characters/interests for the full list"
					},
					"profileImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "New profile image URL (downloaded and saved)"
					},
					"gender": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/gender"
					},
					"orientation": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/orientation"
					},
					"hairColour": {
						"type": "string",
						"description": "Update hair colour. Use GET /api/v1/characters/hair-colours for valid values"
					},
					"eyeColour": {
						"type": "string",
						"description": "Update eye colour. Use GET /api/v1/characters/eye-colours for valid values"
					},
					"bodyType": {
						"type": "string",
						"description": "Update body type. Use GET /api/v1/characters/body-builds for valid values"
					},
					"ethnicity": {
						"type": "string",
						"description": "Update ethnicity. Use GET /api/v1/characters/ethnicities for valid values"
					},
					"hairLength": {
						"type": "string",
						"description": "Update hair length. Use GET /api/v1/characters/hair-lengths for valid values"
					},
					"breastSize": {
						"type": "string",
						"description": "Update breast size (female only). Use GET /api/v1/characters/breast-sizes for valid values"
					},
					"vaginaHair": {
						"type": "string",
						"description": "Update pubic hair style (female only). Use GET /api/v1/characters/vagina-hair for valid values"
					},
					"contentLevel": {
						"$ref": "#/components/schemas/GenerateCharacterRequest/properties/contentLevel"
					},
					"tattoos": {
						"type": "string",
						"description": "Update tattoo description"
					},
					"physicalCharacteristics": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Update physical characteristics (array of strings)"
					},
					"kinks": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Update kinks. Values must match valid terms — use GET /api/v1/characters/kinks for the full list"
					},
					"relationshipStatus": {
						"type": "string",
						"description": "Update relationship status"
					},
					"backstory": {
						"type": "string",
						"description": "Update character backstory"
					},
					"referenceImageUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to new reference image"
					},
					"coverVideoUrl": {
						"type": "string",
						"format": "uri",
						"description": "URL to cover video"
					},
					"voiceUrl": {
						"type": "string",
						"format": "uri",
						"description": "HTTP URL to voice reference audio file (.wav)"
					},
					"audioSpeed": {
						"type": "number",
						"format": "float",
						"minimum": 0.5,
						"maximum": 2,
						"description": "Audio playback speed multiplier"
					},
					"socialHandleInstagram": {
						"type": "string",
						"description": "Instagram handle"
					},
					"socialHandleTiktok": {
						"type": "string",
						"description": "TikTok handle"
					},
					"socialHandleTwitter": {
						"type": "string",
						"description": "X/Twitter handle"
					},
					"socialHandleYoutube": {
						"type": "string",
						"description": "YouTube handle"
					},
					"socialHandleTwitch": {
						"type": "string",
						"description": "Twitch handle"
					},
					"socialHandleTelegram": {
						"type": "string",
						"description": "Telegram handle"
					},
					"earlyAccessEmail": {
						"type": "integer",
						"description": "Early access email entity reference"
					},
					"priorityPrice": {
						"type": "number",
						"format": "float",
						"default": 9.99,
						"description": "Update priority subscription price (field_sub_a)"
					},
					"platinumPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Update platinum subscription price (field_sub_b)"
					},
					"vipPrice": {
						"type": "number",
						"format": "float",
						"default": 39.99,
						"description": "Update vIP subscription price (field_sub_c)"
					},
					"priorityListPrice": {
						"type": "number",
						"format": "float",
						"default": 19.99,
						"description": "Update priority list price (field_list_price_a)"
					},
					"platinumListPrice": {
						"type": "number",
						"format": "float",
						"default": 29.99,
						"description": "Update platinum list price (field_list_price_b)"
					},
					"extraDetails": {
						"type": "string",
						"description": "Additional profile and preference details."
					},
					"roleplayPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"flirtingPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"imageRequestPrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/PromptItem"
						}
					},
					"profileImagePrompts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/ProfileImagePromptItem"
						}
					}
				}
			},
			"PromptItem": {
				"type": "object",
				"required": ["title", "description"],
				"properties": {
					"title": {
						"type": "string",
						"description": "Prompt title."
					},
					"description": {
						"type": "string",
						"description": "Prompt description."
					}
				}
			},
			"ProfileImagePromptItem": {
				"type": "object",
				"required": ["label", "prompt"],
				"properties": {
					"label": {
						"type": "string",
						"description": "Prompt label."
					},
					"prompt": {
						"type": "string",
						"description": "Profile image prompt."
					}
				}
			},
			"ApiError": {
				"type": "object",
				"properties": {
					"error": {
						"type": "string",
						"description": "Machine-readable or concise error."
					},
					"message": {
						"type": "string",
						"description": "Human-readable error details."
					},
					"suggestion": {
						"type": "string",
						"description": "Optional next step."
					}
				}
			},
			"User": {
				"type": "object",
				"properties": {
					"name": {
						"type": "string",
						"description": "User display name."
					},
					"age": {
						"type": "integer",
						"minimum": 18,
						"description": "User age."
					},
					"gender": {
						"type": "string",
						"description": "User gender."
					},
					"location": {
						"type": "string",
						"description": "User location."
					},
					"orientation": {
						"type": "string",
						"description": "User orientation."
					},
					"ab_experiment_group": {
						"type": "string",
						"description": "Optional experiment group."
					}
				}
			},
			"HistoryItem": {
				"type": "object",
				"required": ["role", "content"],
				"properties": {
					"role": {
						"type": "string",
						"description": "Message role, such as user or assistant."
					},
					"content": {
						"description": "Prior message content.",
						"oneOf": [
							{
								"type": "string"
							},
							{
								"$ref": "#/components/schemas/MessageContent"
							}
						]
					}
				}
			},
			"MessageContent": {
				"type": "object",
				"required": ["type"],
				"properties": {
					"type": {
						"type": "string",
						"description": "Message content type."
					},
					"text": {
						"type": "string",
						"description": "Text content."
					},
					"url": {
						"type": "string",
						"description": "Media URL.",
						"format": "uri"
					},
					"prompt": {
						"type": "string",
						"description": "Prompt associated with generated media."
					},
					"level": {
						"type": "string",
						"description": "Content level."
					}
				},
				"additionalProperties": true
			},
			"AsyncGenerationResponse": {
				"type": "object",
				"properties": {
					"message": {
						"type": "string",
						"description": "Generation status message."
					},
					"job_id": {
						"type": "string",
						"description": "Async job identifier."
					},
					"status": {
						"type": "string",
						"enum": ["queued", "processing", "completed", "failed"]
					},
					"presigned_url": {
						"type": "string",
						"description": "Temporary upload or result URL.",
						"format": "uri"
					},
					"message_id": {
						"type": "string",
						"description": "Generated room message identifier."
					}
				}
			},
			"JobStatus": {
				"type": "object",
				"properties": {
					"job_id": {
						"type": "string",
						"description": "Async job identifier."
					},
					"status": {
						"type": "string",
						"enum": ["queued", "processing", "completed", "failed"]
					},
					"url": {
						"type": "string",
						"format": "uri",
						"nullable": true,
						"description": "Generated media URL when complete."
					},
					"results": {
						"type": "object",
						"nullable": true,
						"properties": {
							"image_prompt": {
								"type": "string",
								"description": "Final image prompt used by the worker. On explicit (level 4) image jobs with prompt_enhancement true, this is your own prompt as submitted."
							},
							"detected_level": {
								"type": "integer",
								"description": "Server-detected content level (1–4)."
							},
							"style": {
								"type": "string",
								"nullable": true,
								"description": "Image jobs only: the style the job ran with, \"glam\" (default) or \"amateur\" — or \"artificial\" for a character created with style \"artificial\", regardless of the value requested."
							},
							"style_applied": {
								"type": "boolean",
								"nullable": true,
								"description": "Image jobs only: true when the picture was produced in the requested style (or, for an artificial character with prompt_enhancement true, in the character's own artificial look); false when prompt_enhancement was off or the job took a path where style does not apply (room_id jobs, male characters, or an enhancement failure that fell back to the raw prompt)."
							},
							"position": {
								"type": "string",
								"nullable": true,
								"description": "Explicit (level 4) image jobs with prompt_enhancement true only: the position the picture was rendered in. null everywhere else (levels 1–3, prompt_enhancement false, room_id jobs)."
							},
							"position_source": {
								"type": "string",
								"nullable": true,
								"description": "Set together with position: \"client\" when the request fixed position, \"auto\" when it was detected from the prompt. null otherwise."
							},
							"background": {
								"type": "string",
								"nullable": true,
								"description": "Set together with position: the place the picture was rendered in — the place named in your prompt when there was one, otherwise the style's default room. null otherwise."
							},
							"wardrobe": {
								"type": "string",
								"nullable": true,
								"description": "Explicit (level 4) image jobs with prompt_enhancement true only: the item your request said she is wearing or keeping on (clothing, shoes, jewellery or an accessory such as a collar, glasses or a choker), as understood. null when the request named none; she is then shown fully nude."
							},
							"followup_text": {
								"type": "string",
								"description": "Follow-up text generated for the turn."
							},
							"content": {
								"type": "object",
								"description": "Generated content payload.",
								"nullable": true,
								"additionalProperties": true
							}
						},
						"additionalProperties": true
					},
					"error": {
						"type": "string",
						"nullable": true,
						"description": "Failure reason when status is failed."
					}
				}
			}
		}
	},
	"tags": [
		{
			"name": "Characters V2",
			"description": "Generate and save AI characters."
		},
		{
			"name": "Characters",
			"description": "Allowed values for controlled character attributes."
		},
		{
			"name": "Taxonomy",
			"description": "Short aliases for common character taxonomies."
		},
		{
			"name": "Digital Twins",
			"description": "Create and manage digital twins."
		},
		{
			"name": "Customer Library",
			"description": "Retrieve the authenticated customer's saved characters and twins."
		},
		{
			"name": "Rooms",
			"description": "Manage conversation rooms and messages."
		},
		{
			"name": "Text",
			"description": "Generate synchronous text replies."
		},
		{
			"name": "Audio",
			"description": "Generate synchronous spoken replies."
		},
		{
			"name": "Jobs",
			"description": "Poll asynchronous generation jobs."
		},
		{
			"name": "Images",
			"description": "Generate and regenerate images."
		},
		{
			"name": "Videos",
			"description": "Generate, extend, and regenerate videos."
		},
		{
			"name": "Profile Extraction",
			"description": "Extract creator profiles from interview transcripts."
		},
		{
			"name": "Voices",
			"description": "List available voices."
		},
		{
			"name": "Face Similarity",
			"description": "Compare and embed faces."
		}
	]
}
