curl --request POST \
--url https://api.aurous-labs.com/v1/characters \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"name": "Aurora the Adventurer",
"generate": false,
"upload_ids": [
"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"
],
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"client_reference_id": "bot_8472"
}
'import requests
url = "https://api.aurous-labs.com/v1/characters"
payload = {
"name": "Aurora the Adventurer",
"generate": False,
"upload_ids": ["upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"],
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"client_reference_id": "bot_8472"
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Aurora the Adventurer',
generate: false,
upload_ids: ['upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR'],
attributes: {
gender: 'female',
age: 28,
ethnicity: 'east-asian',
hair_color: 'black',
hair_style: 'shoulder-length straight',
eye_color: 'brown',
body_type: 'athletic',
additional_details: 'small scar above right eyebrow; warm smile'
},
client_reference_id: 'bot_8472'
})
};
fetch('https://api.aurous-labs.com/v1/characters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.aurous-labs.com/v1/characters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Aurora the Adventurer',
'generate' => false,
'upload_ids' => [
'upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR'
],
'attributes' => [
'gender' => 'female',
'age' => 28,
'ethnicity' => 'east-asian',
'hair_color' => 'black',
'hair_style' => 'shoulder-length straight',
'eye_color' => 'brown',
'body_type' => 'athletic',
'additional_details' => 'small scar above right eyebrow; warm smile'
],
'client_reference_id' => 'bot_8472'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.aurous-labs.com/v1/characters"
payload := strings.NewReader("{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.aurous-labs.com/v1/characters")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.aurous-labs.com/v1/characters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}"
response = http.request(request)
puts response.read_body{
"id": "char_01HXMQ7Z3K8Y2NABCDEFGHJKMR",
"object": "character",
"name": "Aurora the Adventurer",
"status": "ready",
"refs": [
{
"pose": "front",
"url": "https://api.aurous-labs.com/storage/.../front.bin?token=…"
}
],
"created_at": "2026-05-08T10:00:00Z",
"updated_at": "2026-05-08T10:00:00Z",
"client_reference_id": "bot_8472",
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"error_message": null,
"aurous_version": "2026-08-26"
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}Create a character
Upload existing ref images or synthesize 4 ref poses from a description.
curl --request POST \
--url https://api.aurous-labs.com/v1/characters \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"name": "Aurora the Adventurer",
"generate": false,
"upload_ids": [
"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"
],
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"client_reference_id": "bot_8472"
}
'import requests
url = "https://api.aurous-labs.com/v1/characters"
payload = {
"name": "Aurora the Adventurer",
"generate": False,
"upload_ids": ["upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"],
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"client_reference_id": "bot_8472"
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Aurora the Adventurer',
generate: false,
upload_ids: ['upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR'],
attributes: {
gender: 'female',
age: 28,
ethnicity: 'east-asian',
hair_color: 'black',
hair_style: 'shoulder-length straight',
eye_color: 'brown',
body_type: 'athletic',
additional_details: 'small scar above right eyebrow; warm smile'
},
client_reference_id: 'bot_8472'
})
};
fetch('https://api.aurous-labs.com/v1/characters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.aurous-labs.com/v1/characters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Aurora the Adventurer',
'generate' => false,
'upload_ids' => [
'upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR'
],
'attributes' => [
'gender' => 'female',
'age' => 28,
'ethnicity' => 'east-asian',
'hair_color' => 'black',
'hair_style' => 'shoulder-length straight',
'eye_color' => 'brown',
'body_type' => 'athletic',
'additional_details' => 'small scar above right eyebrow; warm smile'
],
'client_reference_id' => 'bot_8472'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.aurous-labs.com/v1/characters"
payload := strings.NewReader("{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.aurous-labs.com/v1/characters")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.aurous-labs.com/v1/characters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Aurora the Adventurer\",\n \"generate\": false,\n \"upload_ids\": [\n \"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR\"\n ],\n \"attributes\": {\n \"gender\": \"female\",\n \"age\": 28,\n \"ethnicity\": \"east-asian\",\n \"hair_color\": \"black\",\n \"hair_style\": \"shoulder-length straight\",\n \"eye_color\": \"brown\",\n \"body_type\": \"athletic\",\n \"additional_details\": \"small scar above right eyebrow; warm smile\"\n },\n \"client_reference_id\": \"bot_8472\"\n}"
response = http.request(request)
puts response.read_body{
"id": "char_01HXMQ7Z3K8Y2NABCDEFGHJKMR",
"object": "character",
"name": "Aurora the Adventurer",
"status": "ready",
"refs": [
{
"pose": "front",
"url": "https://api.aurous-labs.com/storage/.../front.bin?token=…"
}
],
"created_at": "2026-05-08T10:00:00Z",
"updated_at": "2026-05-08T10:00:00Z",
"client_reference_id": "bot_8472",
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "east-asian",
"hair_color": "black",
"hair_style": "shoulder-length straight",
"eye_color": "brown",
"body_type": "athletic",
"additional_details": "small scar above right eyebrow; warm smile"
},
"error_message": null,
"aurous_version": "2026-08-26"
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}{
"error": {
"type": "invalid_request",
"code": "balance_too_low",
"message": "Team available balance is 1.5 credits, generation requires 2.0.",
"doc_url": "https://docs.aurous-labs.com/errors#balance_too_low",
"request_id": "req_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"param": "prompt"
}
}POST /v1/characters creates a character — a reusable identity asset you can attach to an image or video generation via character_id (see Create an image or Create a video). There are two mutually exclusive flows:
- Upload flow: pass
upload_ids(1–6) collected fromPOST /v1/characters/uploads/init. The platform moves the bytes to character storage and the response comes back withstatus: ready— immediately usable. - Synthesize flow: pass
generate: trueplus anattributesobject describing who the character is. The platform dispatches a multi-image generation task that produces 4 ref poses (portrait,front,side,back). The response returnsstatus: synthesizingwhile generation is in flight, orstatus: reviewingif synthesis completed before the response returned (typical for fast runs). Either way, pollGET /v1/characters/{id}untilstatus: reviewing, then callPOST /v1/characters/{id}/saveto mark itready.
upload_ids or generate: true — never both, never neither.
Sending both or neither returns 400 parameter_invalid_combination.Upload flow
Use this when you already have ref images. Mint oneupload_id per file via POST /v1/characters/uploads/init, PUT the bytes, then create the character.
curl -X POST https://api.aurous-labs.com/v1/characters \
-H "X-Api-Key: $AUROUS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Aurora",
"upload_ids": [
"upl_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"upl_01HXMQ87RKZQA0YBYV1V47TPS6"
]
}'
const character = await fetch("https://api.aurous-labs.com/v1/characters", {
method: "POST",
headers: {
"X-Api-Key": process.env.AUROUS_API_KEY!,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
name: "Aurora",
upload_ids: ["upl_01HXMQ7Z3K8Y2VNABCDEFGHJKM", "upl_01HXMQ87RKZQA0YBYV1V47TPS6"],
}),
}).then((r) => r.json());
console.log(character.id, character.status); // char_..., "ready"
import os, uuid, requests
r = requests.post(
"https://api.aurous-labs.com/v1/characters",
headers={
"X-Api-Key": os.environ["AUROUS_API_KEY"],
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"name": "Aurora",
"upload_ids": [
"upl_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
"upl_01HXMQ87RKZQA0YBYV1V47TPS6",
],
},
).json()
print(r["id"], r["status"]) # char_..., "ready"
Synthesize flow
Use this when you want the platform to generate the refs from a description. Theattributes object is locked at v1.0 to 7 typed fields plus a free-text additional_details catch-all; on this synthesize flow every field drives generation (on the upload flow only additional_details is used, and only as a best-effort hint — see Upload flow). Synthesize burns credits at create time (4 generation dispatches), so prefer to estimate cost via POST /v1/images/estimate on the equivalent prompt if you need a budget guardrail in your UI.
curl -X POST https://api.aurous-labs.com/v1/characters \
-H "X-Api-Key: $AUROUS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Aurora",
"generate": true,
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "northern european",
"hair_color": "auburn",
"hair_style": "long waves",
"eye_color": "green",
"body_type": "athletic",
"additional_details": "scar across left cheekbone, freckles"
}
}'
const character = await fetch("https://api.aurous-labs.com/v1/characters", {
method: "POST",
headers: {
"X-Api-Key": process.env.AUROUS_API_KEY!,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
name: "Aurora",
generate: true,
attributes: {
gender: "female",
age: 28,
ethnicity: "northern european",
hair_color: "auburn",
hair_style: "long waves",
eye_color: "green",
body_type: "athletic",
additional_details: "scar across left cheekbone, freckles",
},
}),
}).then((r) => r.json());
console.log(character.id, character.status); // char_..., "synthesizing" (or "reviewing" if synth was already done)
import os, uuid, requests
r = requests.post(
"https://api.aurous-labs.com/v1/characters",
headers={
"X-Api-Key": os.environ["AUROUS_API_KEY"],
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"name": "Aurora",
"generate": True,
"attributes": {
"gender": "female",
"age": 28,
"ethnicity": "northern european",
"hair_color": "auburn",
"hair_style": "long waves",
"eye_color": "green",
"body_type": "athletic",
"additional_details": "scar across left cheekbone, freckles",
},
},
).json()
print(r["id"], r["status"]) # char_..., "synthesizing" (or "reviewing" if synth was already done)
Status transitions
| Flow | Initial | After processing | Customer action |
|---|---|---|---|
| Upload | ready | — | Use immediately on POST /v1/images or POST /v1/videos. |
| Synthesize | synthesizing (or reviewing if synth completed before the 201 returned) | reviewing | Poll GET /v1/characters/{id}. When reviewing, call POST /v1/characters/{id}/save to publish; or call POST /v1/characters/{id}/resynthesize to retry. |
| Synthesize (failed) | synthesizing | failed | error_message populated. Call POST /v1/characters/{id}/resynthesize to retry (reuses the stored attributes); if it returns 422 uploads_expired, delete via DELETE /v1/characters/{id} and recreate with fresh upload_ids. |
Limits
- Rate limit: bucket
characters_synthesize— 15 requests/min sustained, 30 burst per team. Both flows ride this bucket since the synthesize discriminator is decided server-side after the request lands. - Idempotency: pass
Idempotency-Key(any opaque value, 1–256 chars). Same key + same body within 24h replays the cached response withAurous-Idempotent-Replayed: true. Same key + different body returns409 idempotency_key_in_use. See Idempotency for details.
Idempotency-Key on synthesize-flow create — it dispatches
4 paid generations, so a network retry without a key can double-charge.Errors
| Code | HTTP | When |
|---|---|---|
parameter_invalid_combination | 400 | Sent both upload_ids and generate: true, or neither. |
upload_invalid | 400 | One of the upload_ids is unknown, expired, or already consumed. |
balance_too_low | 402 | Synthesize-flow dispatch when team credits < cost. |
idempotency_key_in_use | 409 | Same Idempotency-Key was used with a different body. |
too_many_requests | 429 | Burst > 30 or sustained > 15/min. |
Common pitfalls
- The synthesize flow returns 201 immediately, but the character is not usable until it transitions to
ready— either automatically (upload flow) or viaPOST /v1/characters/{id}/save(synthesize flow). CallingPOST /v1/imagesorPOST /v1/videoswith asynthesizingorreviewingcharacter_idreturns400 character_not_ready. - An upload flow with a single ref still works. On the upload flow your reference images define identity, so the 7 typed
attributes(gender, age, etc.) are stored and echoed back but do not shape the generated refs — onlyadditional_detailsis applied there, and only as a best-effort hint (your reference images are the primary signal). The synthesize flow (generate: true) is the opposite: allattributesdrive generation, so don’t send emptyattributesthere. - The
attributesschema is locked for v1.0; new attributes go intoadditional_detailsuntil a v1.1 bump introduces them as typed fields.
Authorizations
Your team API key (starts with al_live_).
Headers
Stripe-style idempotency key (1-256 chars). Same key + same canonical-JSON body returns the cached response with Aurous-Idempotent-Replayed: true. Same key against a different route (e.g. previously used on /v1/images) returns 409 invalid_request / idempotency_key_in_use. Replay window is 24 hours. Absent header is treated as non-idempotent (each call processes anew).
Optional API version pin (YYYY-MM-DD). Omit the header to receive the platform default, currently 2026-08-26.
^\d{4}-\d{2}-\d{2}$"2026-08-26"
Body
Display name for the character (1-80 chars).
1 - 80"Aurora the Adventurer"
Selects the synthesize flow when true (the platform generates 4 ref poses from attributes). When omitted/false, upload_ids must be provided to attach customer-supplied refs. Mutually exclusive with upload_ids.
false
Upload tickets from POST /v1/characters/uploads/init (1-6). When set, the upload flow is used and the server moves bytes from upload storage to character storage on create (uploads are consumed). Mutually exclusive with generate: true.
1 - 6 elements["upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"]
Character attributes. Required when generate: true (synthesize flow), where ALL fields drive generation. Optional on the upload flow, where the 7 typed fields (gender, age, ethnicity, hair_color, hair_style, eye_color, body_type) are stored and echoed back but do NOT shape the generated refs — your uploaded images define identity. additional_details is the exception: it is also applied on the upload flow as a free-text styling hint, but treat it as best-effort — your uploaded reference images are the primary signal, so supply a reference image for any trait you need to guarantee.
Show child attributes
Show child attributes
Optional caller-supplied reference, echoed back on GET /v1/characters/{id} and in every character webhook for this character. Use it to correlate the webhook with the originating record in your system (Stripe-style). Immutable after create. Max 256 chars.
256"bot_8472"
Response
Character accepted; synthesis dispatched (status: synthesizing, refs: []).
Opaque character ID.
"char_01HXMQ7Z3K8Y2NABCDEFGHJKMR"
Discriminator
character "character"
Display name.
"Aurora the Adventurer"
Lifecycle state. synthesizing: synthesize flow running. reviewing: synthesize completed, awaiting POST /:id/save. ready: usable on POST /v1/images. failed: synthesize failed; use POST /:id/resynthesize to retry. deleted: soft-deleted (filtered out of list endpoint).
synthesizing, reviewing, ready, failed, deleted "ready"
Reference images (typically 4 poses).
Show child attributes
Show child attributes
Creation timestamp (ISO 8601).
"2026-05-08T10:00:00Z"
Last-update timestamp (ISO 8601).
"2026-05-08T10:00:00Z"
Caller-supplied reference echoed back (the value sent on create). Null if unset.
"bot_8472"
Character attributes. Null when unset.
Show child attributes
Show child attributes
Machine-readable failure code from the most recent synthesize/resynthesize attempt. One of synthesis_failed, synthesis_timeout, provider_unavailable — a closed set to switch on, not customer-facing prose. Null when the most recent attempt succeeded (or none has run yet). Set on failed, but NOT failed-exclusive: a failed resynthesize restores the character to its prior working status (reviewing) rather than overwriting a good generation, so a reviewing row can carry a non-null code here — check this field for failure, not status. Cleared on the next resynthesize attempt and on success.
null
API contract version applied at the time this row was minted (D25 — frozen for replay across future version bumps).
"2026-08-26"

