curl -X PATCH "https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Mia Torres" }'
const response = await fetch("https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78", {
method: "PATCH",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Mia Torres" }),
});
const { data: character } = await response.json();
import os
import requests
response = requests.patch(
"https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Mia Torres"},
)
character = response.json()["data"]
{
"success": true,
"message": "Character updated",
"data": {
"id": "cm4xa1b2c0001cd34ef56gh78",
"name": "Mia Torres",
"description": "Late 20s, red curly hair, denim jacket",
"voiceProfile": "Warm, slightly husky, speaks fast",
"presetId": "cinematic",
"imageUrl": "https://files.hooked.so/team/public/mia--fid--1a2b.png",
"imageStatus": "COMPLETED",
"createdAt": "2026-09-30T18:00:00.000Z",
"creditsCharged": 0
}
}
{
"success": false,
"message": "The character's photo is still processing; try again when imageStatus is not PROCESSING"
}
Update Character
Change a character’s name, descriptions, style or photo
curl -X PATCH "https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Mia Torres" }'
const response = await fetch("https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78", {
method: "PATCH",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Mia Torres" }),
});
const { data: character } = await response.json();
import os
import requests
response = requests.patch(
"https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Mia Torres"},
)
character = response.json()["data"]
{
"success": true,
"message": "Character updated",
"data": {
"id": "cm4xa1b2c0001cd34ef56gh78",
"name": "Mia Torres",
"description": "Late 20s, red curly hair, denim jacket",
"voiceProfile": "Warm, slightly husky, speaks fast",
"presetId": "cinematic",
"imageUrl": "https://files.hooked.so/team/public/mia--fid--1a2b.png",
"imageStatus": "COMPLETED",
"createdAt": "2026-09-30T18:00:00.000Z",
"creditsCharged": 0
}
}
{
"success": false,
"message": "The character's photo is still processing; try again when imageStatus is not PROCESSING"
}
Overview
Changes one of your team’s characters, the same fields as the character editor in Studio → Characters:name, description, voiceProfile, presetId and the photo. Send only what changes; fields you leave out keep their value. description: null and voiceProfile: null clear them.
The photo works as in Create Character: mediaId (a COMPLETED image of your library), imageUrl (imported after the response) or generatePortrait: true (generated from the description, charged one image), at most one of them. mediaId: null removes the photo. With imageUrl or generatePortrait the answer is 202; poll Get Character until imageStatus is COMPLETED or FAILED.
A portrait uses the character’s description and style after the update, so you can send a new description and generatePortrait: true together.
curl -X PATCH "https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "name": "Mia Torres" }'
const response = await fetch("https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78", {
method: "PATCH",
headers: { "x-api-key": process.env.HOOKED_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Mia Torres" }),
});
const { data: character } = await response.json();
import os
import requests
response = requests.patch(
"https://api.hooked.so/v1/character/cm4xa1b2c0001cd34ef56gh78",
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
json={"name": "Mia Torres"},
)
character = response.json()["data"]
{
"success": true,
"message": "Character updated",
"data": {
"id": "cm4xa1b2c0001cd34ef56gh78",
"name": "Mia Torres",
"description": "Late 20s, red curly hair, denim jacket",
"voiceProfile": "Warm, slightly husky, speaks fast",
"presetId": "cinematic",
"imageUrl": "https://files.hooked.so/team/public/mia--fid--1a2b.png",
"imageStatus": "COMPLETED",
"createdAt": "2026-09-30T18:00:00.000Z",
"creditsCharged": 0
}
}
{
"success": false,
"message": "The character's photo is still processing; try again when imageStatus is not PROCESSING"
}
Errors
| Status | Message | Cause |
|---|---|---|
| 400 | Nothing to update: … | The body has none of the fields above. |
| 400 | field: reason | The same checks as Create Character. |
| 401 | code: "not_authenticated" | Missing or invalid x-api-key. |
| 402 | missing_credentials, subscription_required or INSUFFICIENT_CREDITS | Only with generatePortrait. Nothing was changed. |
| 403 | code: "entitlement_required" | Your team does not have the Hooked app product. |
| 404 | Character not found | No such character in your team (another team’s or a malformed id answers the same). |
| 409 | The character's photo is still processing; … | A new photo was sent while the current one is still being imported or generated. Nothing was changed. |
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]+$Path Parameters
The character id, from List Characters.
Body
New name.
60New visual description; null clears it.
2000New voice description; null clears it.
500New visual style (visual-styles catalog id or your custom-<id>).
A COMPLETED image of your library as the new photo; null removes the photo.
A public https JPEG, PNG, WEBP or GIF. It is imported into your library after the response (free, counted against your storage); imageStatus is PROCESSING until then.
true generates a portrait from description in the character's style, after the response. Charged one image at imageModel (4 credits with the default); given back if it fails.
Image model for generatePortrait (an image-models catalog id). Default gpt_image_2.