curl -X POST "https://api.hooked.so/v1/avatar/create/from-photo" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female" }'
const response = await fetch("https://api.hooked.so/v1/avatar/create/from-photo", {
method: "POST",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Maya", imageUrl: "https://cdn.example.com/team/maya.jpg", gender: "Female" }),
});
const { data } = await response.json();
console.log(data.id, data.status); // use data.id as avatarId
import os
import requests
response = requests.post(
"https://api.hooked.so/v1/avatar/create/from-photo",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female"},
)
data = response.json()["data"]
print(data["id"], data["status"])
{
"success": true,
"message": "Avatar created",
"data": {
"id": "cm7a1v9t20003ab12cd34ef56",
"name": "Maya",
"type": "Realistic",
"gender": "Female",
"age": "Young Adult",
"image": "https://cdn.hooked.so/team/public/image/cm7a1v9t2/maya.png?X-Amz-Signature=...",
"status": "COMPLETED",
"situation": ["Kitchen"],
"emotions": ["Happy"],
"skinTone": "medium",
"isCustom": true,
"source": "custom",
"createdAt": "2026-10-05T09:12:44.000Z",
"usedCredits": 200
}
}
{
"success": false,
"message": "imageUrl: Must be a JPEG, PNG or WEBP image"
}
Create Avatar from Photo
Turn a photo into one of your team’s own avatars
curl -X POST "https://api.hooked.so/v1/avatar/create/from-photo" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female" }'
const response = await fetch("https://api.hooked.so/v1/avatar/create/from-photo", {
method: "POST",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Maya", imageUrl: "https://cdn.example.com/team/maya.jpg", gender: "Female" }),
});
const { data } = await response.json();
console.log(data.id, data.status); // use data.id as avatarId
import os
import requests
response = requests.post(
"https://api.hooked.so/v1/avatar/create/from-photo",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female"},
)
data = response.json()["data"]
print(data["id"], data["status"])
{
"success": true,
"message": "Avatar created",
"data": {
"id": "cm7a1v9t20003ab12cd34ef56",
"name": "Maya",
"type": "Realistic",
"gender": "Female",
"age": "Young Adult",
"image": "https://cdn.hooked.so/team/public/image/cm7a1v9t2/maya.png?X-Amz-Signature=...",
"status": "COMPLETED",
"situation": ["Kitchen"],
"emotions": ["Happy"],
"skinTone": "medium",
"isCustom": true,
"source": "custom",
"createdAt": "2026-10-05T09:12:44.000Z",
"usedCredits": 200
}
}
{
"success": false,
"message": "imageUrl: Must be a JPEG, PNG or WEBP image"
}
Overview
Makes one of your team’s own avatars (an Actor under My Actors in the dashboard) from a photo, the same way Upload a photo does. Send the photo as a publichttps URL (imageUrl) or as an image of your media library (mediaId). The photo is copied, tagged (age group, skin tone, setting, expression, and gender unless you set it), and the avatar is ready at once: the answer is 201 with status: "COMPLETED". Use its id as avatarId in the create endpoints.
curl -X POST "https://api.hooked.so/v1/avatar/create/from-photo" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female" }'
const response = await fetch("https://api.hooked.so/v1/avatar/create/from-photo", {
method: "POST",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Maya", imageUrl: "https://cdn.example.com/team/maya.jpg", gender: "Female" }),
});
const { data } = await response.json();
console.log(data.id, data.status); // use data.id as avatarId
import os
import requests
response = requests.post(
"https://api.hooked.so/v1/avatar/create/from-photo",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Maya", "imageUrl": "https://cdn.example.com/team/maya.jpg", "gender": "Female"},
)
data = response.json()["data"]
print(data["id"], data["status"])
{
"success": true,
"message": "Avatar created",
"data": {
"id": "cm7a1v9t20003ab12cd34ef56",
"name": "Maya",
"type": "Realistic",
"gender": "Female",
"age": "Young Adult",
"image": "https://cdn.hooked.so/team/public/image/cm7a1v9t2/maya.png?X-Amz-Signature=...",
"status": "COMPLETED",
"situation": ["Kitchen"],
"emotions": ["Happy"],
"skinTone": "medium",
"isCustom": true,
"source": "custom",
"createdAt": "2026-10-05T09:12:44.000Z",
"usedCredits": 200
}
}
{
"success": false,
"message": "imageUrl: Must be a JPEG, PNG or WEBP image"
}
Errors
| Status | Message | Cause |
|---|---|---|
| 400 | Invalid JSON body | The body is not JSON. |
| 400 | name: Required | No name (1-60 characters). |
| 400 | imageUrl or mediaId is required / Send imageUrl or mediaId, not both | Send exactly one photo. |
| 400 | imageUrl: Must be a public https URL | Not https, or a local or private address. |
| 400 | imageUrl: Could not download the image (HTTP …) | The URL did not answer the file. |
| 400 | …: Must be a JPEG, PNG or WEBP image / …: The image is over 20 MB | The file itself. |
| 400 | mediaId: Image "…" not found in your library | Not an image of your team’s library. |
| 400 | mediaId: Media "…" is not ready (status PROCESSING) | An import or upload still running. |
| 400 | gender: Must be Female or Male | Leave it out to read it from the photo. |
| 401 | code: "not_authenticated" | Missing or invalid x-api-key. |
| 402 | missing_credentials / subscription_required / INSUFFICIENT_CREDITS | Nothing was downloaded or charged. |
| 403 | code: "entitlement_required" | Your team does not have the Hooked app product. |
Authorizations
Headers
Makes the request safe to retry. 1-255 printable ASCII characters, one per operation (your job id, or a UUID you store), reused on every retry. Within 24 hours the same key with the same body answers with the first response and the header Idempotent-Replayed: true, without running or charging again. Keys are scoped to your team and the endpoint. 5xx answers and refusals before anything ran (401, 402, 403, 409, 429) are not kept. See Idempotency.
1 - 255^[\x20-\x7E]+$Body
The avatar's name (1-60 characters).
60Public https URL of a JPEG, PNG or WEBP photo, up to 20 MB. Required unless mediaId is given; send one of the two.
Id of a COMPLETED image of your media library. Required unless imageUrl is given.
Leave it out to read it from the photo.
Female, Male