> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.morphiaone.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.morphiaone.ai/_mcp/server.

# Submit a Hair color try-on Job

POST https://api.morphiaone.ai/v1/hair/try-ons
Content-Type: application/json

Draft color try-on. Request names one source image Asset and a two-stop color
gradient; the server finds the hair in the photo and recolours only that. A
caller with its own segmentation may add one `hair_mask` Asset instead: it must
match source dimensions, zero pixels mean background and nonzero pixels mean
hair. No haircut style, length or shape transfer is performed. Every input is
snapshotted into the job. Idempotency semantics match Makeup try-on.

Reference: https://docs.morphiaone.ai/api-reference/endpoints/hair/try-ons-create

## Authentication

- `X-API-Key` header (required) — API key for one environment of your account. Keep server-side; never expose in browser clients.

## Request

### Headers

- `Idempotency-Key` (string, optional, nullable)

### Body (application/json)

This endpoint expects an object.

- `color_gradient` (V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradient, required) — Two-stop palette mapped against source hair luminance.
- `inputs` (list of V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaInputsItems, required)

## Response

### 200

Idempotent replay of the job the first submission created

- `completed_at` (datetime, required, nullable)
- `created_at` (datetime, required)
- `error` (JobErrorV1, required, nullable)
- `expires_at` (datetime, required, nullable)
- `id` (string, required)
- `inputs` (list of JobInputRef, required)
- `operation` (enum, required) — What a job does: the operation's authorization scope. An operation is not its output's `type`: `OPERATION_OUTPUT_TYPE` is the one place the two meet.
  - Allowed values: `makeup.apply`, `skin.analyze`, `hair.try_on`
- `output` (JobV1Output, required, nullable)
- `status` (enum, required)
  - Allowed values: `queued`, `running`, `succeeded`, `failed`, `cancelled`
- `updated_at` (datetime, required)
- `livemode` (boolean, optional)

### 202

Job accepted for asynchronous processing

- `completed_at` (datetime, required, nullable)
- `created_at` (datetime, required)
- `error` (JobErrorV1, required, nullable)
- `expires_at` (datetime, required, nullable)
- `id` (string, required)
- `inputs` (list of JobInputRef, required)
- `operation` (enum, required) — What a job does: the operation's authorization scope. An operation is not its output's `type`: `OPERATION_OUTPUT_TYPE` is the one place the two meet.
  - Allowed values: `makeup.apply`, `skin.analyze`, `hair.try_on`
- `output` (JobV1Output, required, nullable)
- `status` (enum, required)
  - Allowed values: `queued`, `running`, `succeeded`, `failed`, `cancelled`
- `updated_at` (datetime, required)
- `livemode` (boolean, optional)

## Errors

### 400 Bad Request Error

Invalid request, inputs or Idempotency-Key

- `error` (ErrorDetail, required)

### 401 Unauthorized Error

Missing API key

- `error` (ErrorDetail, required)

### 402 Payment Required Error

Account credit balance is insufficient

- `error` (ErrorDetail, required)

### 403 Forbidden Error

Key lacks the scope, or is not allowed from this network

- `error` (ErrorDetail, required)

### 404 Not Found Error

Input asset not found

- `error` (ErrorDetail, required)

### 409 Conflict Error

Idempotency conflict

- `error` (ErrorDetail, required)

### 410 Gone Error

Input asset expired

- `error` (ErrorDetail, required)

### 413 Content Too Large Error

Request body exceeds byte limit

- `error` (ErrorDetail, required)

### 429 Too Many Requests Error

Admission limit exceeded

- `error` (ErrorDetail, required)

### 503 Service Unavailable Error

Service capacity or dependency unavailable

- `error` (ErrorDetail, required)

## Types

### V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradient

Two-stop palette mapped against source hair luminance.

- `dark` (V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradientDark, required)
- `light` (V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradientLight, required)

### V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaInputsItems

- `role`: `source` (AssetInputRef)
  - `asset_id` (string, required)
- `role`: `hair_mask` (HairMaskAssetInputRef)
  - `asset_id` (string, required)

### JobErrorV1

- `code` (enum, required)
  - Allowed values: `INPUT_UNPROCESSABLE`, `INPUT_EXPIRED`, `PROCESSING_FAILED`
- `message` (string, required)

### JobInputRef

One input as a job reports it: an open role and a tolerant shape.

- `asset_id` (string, required)
- `role` (string, required)

### JobV1Output

- `type`: `hair.try_on` (HairTryOnOutput)
  - `applied` (HairTryOnApplied, required)
  - `image` (ResultAsset, required) — One named part of a job output, served through the V1 asset content route.
  - `revision` (integer, optional, default: 1)
- `type`: `makeup.try_on` (MakeupTryOnOutput)
  - `image` (ResultAsset, required) — One named part of a job output, served through the V1 asset content route.
  - `revision` (integer, optional, default: 1)
- `type`: `skin.analysis` (SkinAnalysisOutput)
  - `concerns` (list of SkinConcernResult, required)
  - `analysis_version` (string, optional, default: skin-2026-09-draft.1)
  - `visualizations` (list of ResultAsset, optional, default: [])

### ErrorDetail

- `code` (string, required)
- `message` (string, required)
- `request_id` (string, required)
- `details` (ErrorDetailDetails, optional)
- `doc_url` (string, optional, nullable)
- `retryable` (boolean, optional, nullable)

### V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradientDark

- `color` (string, required)
- `opacity` (double, optional, default: 1)

### V1HairTryOnsPostRequestBodyContentApplicationJsonSchemaColorGradientLight

- `color` (string, required)
- `opacity` (double, optional, default: 1)

### HairTryOnApplied

- `color_gradient` (HairAppliedColorGradient, required)

### ResultAsset

One named part of a job output, served through the V1 asset content route.

- `expires_at` (datetime, required)
- `href` (string, required)
- `id` (string, required)
- `media_type` (enum, required)
  - Allowed values: `image/png`, `image/webp`
- `role` (string, required)
- `size_bytes` (integer, required)
- `height` (integer, optional, nullable)
- `width` (integer, optional, nullable)

### SkinConcernResult

- `concern` (enum, required)
  - Allowed values: `acne`, `dark_circle`, `eye_bag`, `redness`, `oiliness`, `wrinkles`, `firmness`, `moisture`, `pore`, `tear_trough`, `hyper_pigmentation`, `droopy_upper_eyelid`
- `score` (double, required, nullable)
- `status` (enum, required)
  - Allowed values: `measured`, `unavailable`
- `unavailable_reason` (enum, required, nullable)
  - Allowed values: `not_measured`, `not_measurable`

### ErrorDetailDetails

### HairAppliedColorGradient

- `dark` (HairAppliedColorStop, required)
- `light` (HairAppliedColorStop, required)

### HairAppliedColorStop

- `color` (string, required)
- `opacity` (double, required)

## Examples

### Hair_tryOnsCreate_example

**Request**

```json
{
  "color_gradient": {
    "dark": {
      "color": "string"
    },
    "light": {
      "color": "string"
    }
  },
  "inputs": [
    {
      "asset_id": "string",
      "role": "string"
    }
  ]
}
```

**Response**

```json
{
  "completed_at": null,
  "created_at": "2026-09-24T10:00:05Z",
  "error": null,
  "expires_at": "2026-09-25T10:00:05Z",
  "id": "job_5c7e9g2j4m6p8r1t3v5x7z9b2d",
  "inputs": [
    {
      "asset_id": "asset_6pa8ad0ds2t7g1z6pg4tcr4zqp",
      "role": "source"
    },
    {
      "asset_id": "asset_eyh67f8tc55kdrayf5zka7d7e8",
      "role": "hair_mask"
    }
  ],
  "operation": "hair.try_on",
  "output": null,
  "status": "queued",
  "updated_at": "2026-09-24T10:00:05Z"
}
```

**SDK Code**

```python Hair_tryOnsCreate_example
import requests

url = "https://api.morphiaone.ai/v1/hair/try-ons"

payload = {
    "color_gradient": {
        "dark": { "color": "string" },
        "light": { "color": "string" }
    },
    "inputs": [
        {
            "asset_id": "string",
            "role": "string"
        }
    ]
}
headers = {
    "X-API-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Hair_tryOnsCreate_example
const url = 'https://api.morphiaone.ai/v1/hair/try-ons';
const options = {
  method: 'POST',
  headers: {'X-API-Key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"color_gradient":{"dark":{"color":"string"},"light":{"color":"string"}},"inputs":[{"asset_id":"string","role":"string"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Hair_tryOnsCreate_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.morphiaone.ai/v1/hair/try-ons"

	payload := strings.NewReader("{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-API-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Hair_tryOnsCreate_example
require 'uri'
require 'net/http'

url = URI("https://api.morphiaone.ai/v1/hair/try-ons")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Hair_tryOnsCreate_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.morphiaone.ai/v1/hair/try-ons")
  .header("X-API-Key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}")
  .asString();
```

```php Hair_tryOnsCreate_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.morphiaone.ai/v1/hair/try-ons', [
  'body' => '{
  "color_gradient": {
    "dark": {
      "color": "string"
    },
    "light": {
      "color": "string"
    }
  },
  "inputs": [
    {
      "asset_id": "string",
      "role": "string"
    }
  ]
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp Hair_tryOnsCreate_example
using RestSharp;

var client = new RestClient("https://api.morphiaone.ai/v1/hair/try-ons");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Hair_tryOnsCreate_example
import Foundation

let headers = [
  "X-API-Key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "color_gradient": [
    "dark": ["color": "string"],
    "light": ["color": "string"]
  ],
  "inputs": [
    [
      "asset_id": "string",
      "role": "string"
    ]
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.morphiaone.ai/v1/hair/try-ons")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Example 2

**Request**

```json
{
  "color_gradient": {
    "dark": {
      "color": "string"
    },
    "light": {
      "color": "string"
    }
  },
  "inputs": [
    {
      "asset_id": "string",
      "role": "string"
    }
  ]
}
```

**Response**

```json
{
  "completed_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "error": {
    "code": "INPUT_UNPROCESSABLE",
    "message": "string"
  },
  "expires_at": "2024-01-15T09:30:00Z",
  "id": "string",
  "inputs": [
    {
      "asset_id": "string",
      "role": "string"
    }
  ],
  "operation": "makeup.apply",
  "output": {
    "type": "makeup.try_on",
    "image": {
      "expires_at": "2024-01-15T09:30:00Z",
      "href": "string",
      "id": "string",
      "media_type": "image/png",
      "role": "string",
      "size_bytes": 1,
      "height": 1,
      "width": 1
    },
    "revision": 1
  },
  "status": "queued",
  "updated_at": "2024-01-15T09:30:00Z",
  "livemode": true
}
```

**SDK Code**

```python
import requests

url = "https://api.morphiaone.ai/v1/hair/try-ons"

payload = {
    "color_gradient": {
        "dark": { "color": "string" },
        "light": { "color": "string" }
    },
    "inputs": [
        {
            "asset_id": "string",
            "role": "string"
        }
    ]
}
headers = {
    "X-API-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.morphiaone.ai/v1/hair/try-ons';
const options = {
  method: 'POST',
  headers: {'X-API-Key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"color_gradient":{"dark":{"color":"string"},"light":{"color":"string"}},"inputs":[{"asset_id":"string","role":"string"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.morphiaone.ai/v1/hair/try-ons"

	payload := strings.NewReader("{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-API-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.morphiaone.ai/v1/hair/try-ons")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.morphiaone.ai/v1/hair/try-ons")
  .header("X-API-Key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.morphiaone.ai/v1/hair/try-ons', [
  'body' => '{
  "color_gradient": {
    "dark": {
      "color": "string"
    },
    "light": {
      "color": "string"
    }
  },
  "inputs": [
    {
      "asset_id": "string",
      "role": "string"
    }
  ]
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.morphiaone.ai/v1/hair/try-ons");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"color_gradient\": {\n    \"dark\": {\n      \"color\": \"string\"\n    },\n    \"light\": {\n      \"color\": \"string\"\n    }\n  },\n  \"inputs\": [\n    {\n      \"asset_id\": \"string\",\n      \"role\": \"string\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "X-API-Key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "color_gradient": [
    "dark": ["color": "string"],
    "light": ["color": "string"]
  ],
  "inputs": [
    [
      "asset_id": "string",
      "role": "string"
    ]
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.morphiaone.ai/v1/hair/try-ons")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```