{
  "openapi": "3.0.0",
  "info": {
    "title": "AI Hair Color Virtual Try-On",
    "description": "# Overview\nExplore a wide range of hair colors with our hair color changer! Try the hair color you've always dreamed of and experiment with new shades you’ve never tried before. Easily adjust the intensity of your chosen color with sliders for a customized look.\n\n   * Upload Your Image\n\nUpload the photo you want to change hair color for.\n\n   * Choose Preset Colors or Customize by Pattern and Palettes\n\nChoose from predefined color presets or fine tune by adjusting the ombre coverage and blend for unlimited possibilities!\n\n> **Warning:** If both a preset and pattern + palettes are specified, the preset will take priority.\n\n> **Warning:** Your source image needs to contain the hair section for dyeing, so double-check before applying. Make sure your source image includes the hair area you want to dye — it's your responsibility to get it right.\n\n![](https://bcw-media.s3.ap-northeast-1.amazonaws.com/hair_color_s2_poster_dt_v2_49198cabc0.png)\n\n![](https://bcw-media.s3.ap-northeast-1.amazonaws.com/01_1_1_8365c3b503.jpg)\n\n![](https://bcw-media.s3.ap-northeast-1.amazonaws.com/01_2_1_abfcdb7eba.jpg)\n\n## File Specs & Errors\n* Supported Formats & Dimensions\n\n|AI Feature|Supported Dimensions|Supported File Size|Supported Formats|\n|  ----  | ----  | ----  | ----  |\n|AI Hair Color|long side < 1920, face width >= 100|< 10MB|jpg/jpeg/png|\n\n* Error Codes\n\n|Error Code|Description|\n|  ----  | ----  |\n|error_below_min_image_size|the size of the source image is smaller than minimum (expect: width >= 320px, height >= 320px)\n|error_exceed_max_image_size|the size of the source image is larger than maximum (expect: width < 1920px, height < 1080px)\n\n* Environment & Dependency\n\n| Sample Code Language / Tool | Recommended Runtime Versions |\n|---|---|\n| cURL | - bash >= 3.2</br>   - curl >= 7.58 (modern TLS/HTTP support)</br>   - jq >= 1.6 (robust JSON parsing) |\n| Node.js (JavaScript) | Node >= 18 (for global fetch) |\n| JavaScript | - Chrome / Edge >= 80</br>   - Firefox >= 74</br>   - Safari >= 13.1 |\n| PHP | PHP >= 7.4 (for modern TLS/compat), ext-curl (recommended) or allow_url_fopen=On + ext-openssl, ext-json |\n| Python | Python >= 3.10 (for f-strings), requests >= 2.20.0 |\n| Java | Java 11+ (for HttpClient), Jackson Databind >= 2.12.0 |\n\n---\n\n## Unit Consumption\n\n| AI Feature | Unit Consumed |\n|---|---|\n| AI Hair Color Virtual Try-On V1.0 | 1 unit for Full Mode <br> 1 unit for Ombre Mode |\n\n---\n",
    "version": "",
    "termsOfService": "https://www.makeupar.com/perfectbeauty/youcam/terms-of-service-api",
    "contact": {
      "email": "YouCamOnlineEditor_API@perfectcorp.com"
    },
    "license": {
      "name": "Privacy policy",
      "url": "https://www.makeupar.com/perfectbeauty/youcam/privacy-policy-api"
    }
  },
  "servers": [
    {
      "url": "https://yce-api-01.makeupar.com"
    }
  ],
  "tags": [
    {
      "name": "V1.0",
      "description": "Change hair color on uploaded images using AI processing, supporting presets and custom palettes."
    }
  ],
  "paths": {
    "/s2s/v2.0/task/hair-color": {
      "post": {
        "summary": "Run a Hair Color task.",
        "description": "This endpoint initiates the hair color change process. You must provide a source file (via URL or File ID) and specify the color settings (preset, pattern, or palettes). The task will be processed asynchronously, and you can check its status using the task_id returned in this response.\n",
        "tags": [
          "V1.0"
        ],
        "security": [
          {
            "BearerAuthenticationV2": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunHairColorTaskV2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful execution of the Hair Color task",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BasicRunTaskResponseV2"
                }
              }
            }
          },
          "400": {
            "description": "Failed execution of task",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/responses/RunError"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/s2s/v2.0/task/hair-color/{task_id}": {
      "get": {
        "summary": "Check a Hair Color task status.",
        "tags": [
          "V1.0"
        ],
        "security": [
          {
            "BearerAuthenticationV2": []
          }
        ],
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "grH0CvsgXuAIHLUzD0V1Ol34hoet3R1tvdbtiVHrDb6_UqCLKIejAIajwxrhOAfe"
            },
            "description": "ID of task to check"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful check of the Hair Color task status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskStatusResponseV2"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidTaskId"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "500": {
            "$ref": "#/components/responses/TaskTimeout"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuthenticationV2": {
        "type": "http",
        "scheme": "bearer",
        "description": "Use the standard 'Bearer authentication'. Put your 'API Key' in header: `Authorization:Bearer YOUR_API_KEY`. Notice that there is ' ' a space between 'Bearer' and the 'YOUR_API_KEY'."
      }
    },
    "schemas": {
      "EngineErrorCode": {
        "type": "string",
        "nullable": true,
        "enum": [
          "exceed_max_filesize",
          "invalid_parameter",
          "error_download_image",
          "error_decode_image",
          "error_nsfw_content_detected",
          "error_inference",
          "unknown_internal_error"
        ],
        "description": "Errors:\n\n- `exceed_max_filesize` - Input file size exceeds the maximum limit\n\n- `invalid_parameter` - Invalid parameter value\n\n- `error_download_image` - Download source image error\n\n- `error_decode_image` - Decode source image error\n\n- `unknown_internal_error` - Others\n"
      },
      "FileV1.1": {
        "title": "File V1.1",
        "description": "This object represents a file.",
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "content_type",
                "file_name",
                "file_size"
              ],
              "properties": {
                "content_type": {
                  "type": "string",
                  "example": "image/jpg",
                  "description": "Content MIME type for this file. Currently available values are listed in the enum."
                },
                "file_name": {
                  "type": "string",
                  "example": "my-selfie.jpg",
                  "description": "Name of this file"
                },
                "file_size": {
                  "type": "integer",
                  "example": 50000,
                  "description": "Content length for this file in bytes. Should not be larger than 10MB."
                }
              }
            }
          }
        }
      },
      "BasicFileResponse": {
        "type": "object",
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "content_type": {
                  "type": "string",
                  "example": "image/jpg",
                  "description": "Content MIME type for this file."
                },
                "file_name": {
                  "type": "string",
                  "example": "my-selfie.jpg",
                  "description": "Name of this file"
                },
                "file_id": {
                  "type": "string",
                  "example": "U8aqJbsXGT537jtGnEDFHqxdDXqh8+oTF/cSkLimzuvVwMP+Jb1XbjPsf7ZgUgLY",
                  "description": "ID of this file. Other run task API will need this `file_id`."
                },
                "requests": {
                  "type": "array",
                  "description": "Using upload `url`, `headers`, `method` below to upload file. After completion, the `file_id` is used to proceed with calling run task API.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "headers": {
                        "type": "object",
                        "example": {
                          "Content-Type": "image/jpg",
                          "Content-Length": 50000
                        },
                        "description": "Headers to include when uploading the file"
                      },
                      "url": {
                        "type": "string",
                        "example": "https://example.com/presigned-upload-url",
                        "description": "URL to upload this file"
                      },
                      "method": {
                        "type": "string",
                        "example": "PUT",
                        "description": "HTTP method to upload this file"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "FileResponseV2": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "example": 200,
            "description": "Response status"
          },
          "data": {
            "$ref": "#/components/schemas/BasicFileResponse"
          }
        }
      },
      "BasicRunTaskV2SrcFileUrl": {
        "title": "Run task with src file url",
        "type": "object",
        "required": [
          "src_file_url"
        ],
        "properties": {
          "src_file_url": {
            "type": "string",
            "description": "Url of the file to run task. The url should be publicly accessible.",
            "example": "https://example.com/selfie.jpg"
          }
        }
      },
      "BasicRunTaskV2SrcFileId": {
        "title": "Run task with src file ID",
        "type": "object",
        "required": [
          "src_file_id"
        ],
        "properties": {
          "src_file_id": {
            "type": "string",
            "description": "ID of file to run task. File ID from upload file API.",
            "example": "pfNK5PuRe0MrwLHcGA3DOmB1ahwfXTbYHjv+KoBIxbE="
          }
        }
      },
      "BasicRunTaskV2": {
        "title": "BasicRunTaskV2",
        "anyOf": [
          {
            "$ref": "#/components/schemas/BasicRunTaskV2SrcFileUrl"
          },
          {
            "$ref": "#/components/schemas/BasicRunTaskV2SrcFileId"
          }
        ]
      },
      "RunHairColorTaskParams": {
        "type": "object",
        "properties": {
          "preset": {
            "type": "string",
            "description": "(optional; required if neither \"pattern\" nor \"palettes\" is provided)\nDefault hair color preset. Available for full mode: ['Jet Black',\n'Chocolate Brown', 'Honey Blonde', 'Platinum Blonde', 'Ash Gray',\n'Rose Gold', 'Burgundy', 'Copper Red', 'Lavender', 'Teal Blue']\nAvailable for ombre mode: ['Dark Brown/Caramel Blonde', 'Jet\nBlack/Silver Gray', 'Ash Brown/Lavender', 'Rose Gold/Peach Blonde',\n'Burgundy/Magenta Pink', 'Deep Blue/Teal Green', 'Plum Purple/Pastel\nLilac', 'Copper Red/Golden Blonde', 'Dark Gray/Ice Blonde',\n'Midnight Blue/Denim Blue']\n",
            "enum": [
              "Jet Black",
              "Chocolate Brown",
              "Honey Blonde",
              "Platinum Blonde",
              "Ash Gray",
              "Rose Gold",
              "Burgundy",
              "Copper Red",
              "Lavender",
              "Teal Blue",
              "Dark Brown/Caramel Blonde",
              "Jet Black/Silver Gray",
              "Ash Brown/Lavender",
              "Rose Gold/Peach Blonde",
              "Burgundy/Magenta Pink",
              "Deep Blue/Teal Green",
              "Plum Purple/Pastel Lilac",
              "Copper Red/Golden Blonde",
              "Dark Gray/Ice Blonde",
              "Midnight Blue/Denim Blue"
            ]
          },
          "pattern": {
            "type": "object",
            "description": "(optional; required if \"preset\" is not provided)",
            "required": [
              "name"
            ],
            "properties": {
              "name": {
                "type": "string",
                "enum": [
                  "ombre",
                  "full"
                ],
                "description": "Pattern type (required)"
              },
              "blend_strength": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "(optional; required for ombre) Blend strength (0–100)"
              },
              "line_offset": {
                "type": "number",
                "minimum": -0.99,
                "maximum": 0.99,
                "description": "(optional; required for ombre) Vertical offset (–0.99 to 0.99).\n0.99: top color covers 99% of bottom half;   –0.99: bottom color\ncovers 99% of top half\n"
              },
              "coloring_section": {
                "type": "string",
                "enum": [
                  "top"
                ],
                "description": "(optional; required for ombre) Section to color"
              }
            }
          },
          "palettes": {
            "type": "array",
            "description": "(optional; required if \"preset\" is not provided)   Array length 1\nfor full mode, 2 for ombre mode\n",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "object",
              "required": [
                "color"
              ],
              "properties": {
                "color": {
                  "type": "string",
                  "pattern": "^#([A-Fa-f0-9]{6})$",
                  "description": "Hex color code"
                },
                "color_intensity": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Color intensity (0–100)"
                },
                "shine_intensity": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Shine intensity (0–100)"
                }
              }
            }
          }
        }
      },
      "RunHairColorTaskV2": {
        "title": "RunHairColorTaskV2",
        "description": "This object represents a run AI Hair Color task.",
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/BasicRunTaskV2"
          },
          {
            "$ref": "#/components/schemas/RunHairColorTaskParams"
          }
        ]
      },
      "BasicRunTaskResponseV2": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "description": "Response status",
            "example": 200
          },
          "data": {
            "type": "object",
            "properties": {
              "task_id": {
                "type": "string",
                "description": "ID of this task. Task result is valid to query by this ID for 24 hours.",
                "example": "grH0CvsgXuAIHLUzD0V1Ol34hoet3R1tvdbtiVHrDb6_UqCLKIejAIajwxrhOAfe"
              }
            }
          }
        }
      },
      "TaskStatusResponseBodySingleUrlResultsV2": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "URL to download this result. Valid for 2 hours",
            "example": "https://example.com/sample-result-url"
          }
        }
      },
      "TaskStatusResponseV2": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "description": "Response status",
            "example": 200
          },
          "data": {
            "type": "object",
            "properties": {
              "task_status": {
                "type": "string",
                "enum": [
                  "running",
                  "success",
                  "error"
                ],
                "description": "Status of this task"
              },
              "error": {
                "$ref": "#/components/schemas/EngineErrorCode"
              },
              "error_message": {
                "type": "string",
                "description": "Detailed description of error"
              },
              "results": {
                "$ref": "#/components/schemas/TaskStatusResponseBodySingleUrlResultsV2"
              }
            }
          }
        }
      }
    },
    "responses": {
      "InvalidParameters": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "integer",
                  "example": 400,
                  "description": "Response status"
                },
                "error": {
                  "type": "string",
                  "description": "Error message",
                  "example": "The operation could not be completed"
                },
                "error_code": {
                  "type": "string",
                  "enum": [
                    "InvalidParameters"
                  ]
                }
              }
            }
          }
        }
      },
      "InvalidApiKey": {
        "description": "Invalid API Key or Inactive API Key or Expired API Key",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "integer",
                  "example": 401,
                  "description": "Response status"
                },
                "error_code": {
                  "type": "string",
                  "enum": [
                    "InvalidApiKey",
                    "InactiveApiKey",
                    "ExpiredApiKey"
                  ]
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too many requests within a given amount of time"
      },
      "RunError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "description": "Response status",
            "example": 400
          },
          "error": {
            "type": "string",
            "description": "Error message",
            "example": "The operation could not be completed"
          },
          "error_code": {
            "type": "string",
            "enum": [
              "CreditInsufficiency",
              "InvalidStyleGroup",
              "InvalidStyle",
              "BadRequest",
              "InvalidParameters"
            ]
          }
        }
      },
      "TaskTimeout": {
        "description": "The task has no response in the expected time",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "integer",
                  "description": "Response status",
                  "example": 500
                },
                "error_code": {
                  "type": "string",
                  "enum": [
                    "TaskTimeout"
                  ]
                }
              }
            }
          }
        }
      },
      "InvalidTaskId": {
        "description": "Invalid task id",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "integer",
                  "description": "Response status",
                  "example": 400
                },
                "error_code": {
                  "type": "string",
                  "enum": [
                    "InvalidTaskId"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}