curl --request PATCH \
--url https://api.onlyx.ai/v1/creators/{creatorId}/persona \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"age": 24,
"city": "Austin",
"hardLimits": [
"No meetups."
],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"nickname": null,
"occupation": "Yoga instructor",
"personaName": "Mia",
"timezone": "America/Chicago"
}
'import requests
url = "https://api.onlyx.ai/v1/creators/{creatorId}/persona"
payload = {
"age": 24,
"city": "Austin",
"hardLimits": ["No meetups."],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"nickname": None,
"occupation": "Yoga instructor",
"personaName": "Mia",
"timezone": "America/Chicago"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
age: 24,
city: 'Austin',
hardLimits: ['No meetups.'],
lore: 'Grew up by the beach, moved to Austin for college and stayed for the music.',
nickname: null,
occupation: 'Yoga instructor',
personaName: 'Mia',
timezone: 'America/Chicago'
})
};
fetch('https://api.onlyx.ai/v1/creators/{creatorId}/persona', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"accountContext": "Paid page. The wall has photos; videos are sold in messages.",
"additionalLanguages": [
"es"
],
"age": 24,
"aiDisclosureEnabled": false,
"aiDisclosureText": "",
"allowTypos": true,
"archetype": "girl_next_door",
"boundaries": [
"Does not talk about her family."
],
"capitalization": "lowercase",
"city": "Austin",
"completeness": 86,
"contentMenu": [
"lingerie photos",
"shower videos"
],
"contentNotes": "No face in explicit videos.",
"customContentEnabled": true,
"customPhotoDeliveryDays": 3,
"customVideoDeliveryDays": 7,
"emojiPolicy": "some",
"exemplars": [
"heyy you 😘 how was your day?"
],
"handoffCatalogue": [
{
"default": true,
"key": "welfare.self_harm",
"label": "Suicidal or self-harm language",
"locked": true,
"reason": "welfare"
},
{
"default": true,
"key": "payment.refund_demand",
"label": "Asks for a refund",
"locked": false,
"reason": "payment_dispute"
},
{
"default": false,
"key": "real_world.meet_request",
"label": "Asks to meet",
"locked": false,
"reason": "real_world"
}
],
"handoffEffective": [
"welfare.self_harm",
"welfare.immediate_danger",
"prohibited.underage",
"prohibited.animals_illegal",
"real_world.stalking",
"real_world.threat",
"payment.fraud_threat",
"payment.refund_demand",
"payment.double_charge",
"payment.not_received",
"payment.custom_overdue",
"whale.broken_promise",
"whale.feels_scammed"
],
"handoffKinds": null,
"hardLimits": [
"No meetups.",
"No custom videos with other people."
],
"interests": [
"hiking",
"live music",
"tacos"
],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"messageSplitting": "sometimes",
"nickname": "Mimi",
"occupation": "Yoga instructor",
"origin": "San Diego",
"personaName": "Mia",
"physicalDescription": "Blonde, 5'6\", athletic, small tattoo on her left wrist.",
"priceCustomPhotoCents": 2500,
"priceCustomVideoMinCents": 5000,
"priceCustomVideoPerMinCents": 1000,
"primaryLanguage": "en",
"punctuation": "relaxed",
"readyForAi": true,
"requiredMissing": [],
"timezone": "America/Chicago",
"typingSpeed": "natural",
"updatedAt": "2026-09-26T14:02:31.000Z"
}Update the AI persona
Changes the AI persona. It is a true PATCH: fields you leave out stay as they are, null clears a field, and a field name that is not part of the persona is refused with 400 VALIDATION_ERROR. Lists (boundaries, hardLimits, exemplars, …) are replaced whole, timezone must be a valid IANA zone and primaryLanguage a BCP-47 tag. The AI reads the persona fresh for every reply, so a change applies to the next message it writes; nothing is sent to fans by this call. The AI needs personaName, age, city, occupation and lore before readyForAi becomes true.
Scope: requires ai:write.
Rate limit: the general limit of 120 requests per 60 seconds per credential.
Idempotency: send an Idempotency-Key header to make retries safe: a replay within 24 hours returns the original response with Idempotent-Replayed: true.
curl --request PATCH \
--url https://api.onlyx.ai/v1/creators/{creatorId}/persona \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"age": 24,
"city": "Austin",
"hardLimits": [
"No meetups."
],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"nickname": null,
"occupation": "Yoga instructor",
"personaName": "Mia",
"timezone": "America/Chicago"
}
'import requests
url = "https://api.onlyx.ai/v1/creators/{creatorId}/persona"
payload = {
"age": 24,
"city": "Austin",
"hardLimits": ["No meetups."],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"nickname": None,
"occupation": "Yoga instructor",
"personaName": "Mia",
"timezone": "America/Chicago"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
age: 24,
city: 'Austin',
hardLimits: ['No meetups.'],
lore: 'Grew up by the beach, moved to Austin for college and stayed for the music.',
nickname: null,
occupation: 'Yoga instructor',
personaName: 'Mia',
timezone: 'America/Chicago'
})
};
fetch('https://api.onlyx.ai/v1/creators/{creatorId}/persona', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"accountContext": "Paid page. The wall has photos; videos are sold in messages.",
"additionalLanguages": [
"es"
],
"age": 24,
"aiDisclosureEnabled": false,
"aiDisclosureText": "",
"allowTypos": true,
"archetype": "girl_next_door",
"boundaries": [
"Does not talk about her family."
],
"capitalization": "lowercase",
"city": "Austin",
"completeness": 86,
"contentMenu": [
"lingerie photos",
"shower videos"
],
"contentNotes": "No face in explicit videos.",
"customContentEnabled": true,
"customPhotoDeliveryDays": 3,
"customVideoDeliveryDays": 7,
"emojiPolicy": "some",
"exemplars": [
"heyy you 😘 how was your day?"
],
"handoffCatalogue": [
{
"default": true,
"key": "welfare.self_harm",
"label": "Suicidal or self-harm language",
"locked": true,
"reason": "welfare"
},
{
"default": true,
"key": "payment.refund_demand",
"label": "Asks for a refund",
"locked": false,
"reason": "payment_dispute"
},
{
"default": false,
"key": "real_world.meet_request",
"label": "Asks to meet",
"locked": false,
"reason": "real_world"
}
],
"handoffEffective": [
"welfare.self_harm",
"welfare.immediate_danger",
"prohibited.underage",
"prohibited.animals_illegal",
"real_world.stalking",
"real_world.threat",
"payment.fraud_threat",
"payment.refund_demand",
"payment.double_charge",
"payment.not_received",
"payment.custom_overdue",
"whale.broken_promise",
"whale.feels_scammed"
],
"handoffKinds": null,
"hardLimits": [
"No meetups.",
"No custom videos with other people."
],
"interests": [
"hiking",
"live music",
"tacos"
],
"lore": "Grew up by the beach, moved to Austin for college and stayed for the music.",
"messageSplitting": "sometimes",
"nickname": "Mimi",
"occupation": "Yoga instructor",
"origin": "San Diego",
"personaName": "Mia",
"physicalDescription": "Blonde, 5'6\", athletic, small tattoo on her left wrist.",
"priceCustomPhotoCents": 2500,
"priceCustomVideoMinCents": 5000,
"priceCustomVideoPerMinCents": 1000,
"primaryLanguage": "en",
"punctuation": "relaxed",
"readyForAi": true,
"requiredMissing": [],
"timezone": "America/Chicago",
"typingSpeed": "natural",
"updatedAt": "2026-09-26T14:02:31.000Z"
}Authorizations
An API key (onx_sk_…) created in OnlyX under Settings → API & MCP, or an OAuth access token (onx_at_…) issued to a connected app. Send it as Authorization: Bearer <credential>. API keys may also be sent as X-API-Key: <key>.
Headers
Optional. A unique value (8 to 64 letters, digits, - or _; a UUID is ideal) that makes a retry safe: the same key with the same request within 24 hours returns the stored response, with Idempotent-Replayed: true, instead of doing the work twice. The same key with a different request is 409 IDEMPOTENCY_KEY_REUSED; while the first request still runs it is 409 IDEMPOTENCY_IN_PROGRESS. A malformed key is 400 VALIDATION_ERROR.
8 - 64^[A-Za-z0-9_-]{8,64}$Path Parameters
The creator id (cre_…).
Body
A true PATCH: fields you leave out are unchanged; null clears a field (text becomes empty,
lists become empty, numbers become unset, archetype/primaryLanguage/timezone go back to
their defaults, handoffKinds goes back to the recommended set); a field that is not listed here
is a 400 VALIDATION_ERROR. Choice fields and switches cannot be cleared, only set.
Up to 1,000 characters. null clears it.
1000Up to 50 BCP-47 tags. Replaces the whole list; null empties it.
502 - 2018 to 99. null clears it.
18 <= x <= 99Cannot be cleared.
Up to 500 characters. null clears it.
500Cannot be cleared.
Up to 60 characters. null puts back the default girl_next_door.
60Up to 50 items of up to 500 characters. Replaces the whole list; null empties it.
50500Cannot be cleared.
normal, lowercase, casual_mix Up to 120 characters. null clears it.
120Up to 50 items of up to 500 characters. Replaces the whole list; null empties it.
50500Up to 1,000 characters. null clears it.
1000Cannot be cleared.
1 to 60. null uses the default of 3.
1 <= x <= 601 to 60. null uses the default of 7.
1 <= x <= 60Cannot be cleared.
none, rare, some, heavy Up to 50 items of up to 500 characters. Replaces the whole list; null empties it.
50500Keys from handoffCatalogue. null means the recommended set; [] means only the kinds that can never be switched off. Locked kinds always apply, whatever you send; an unknown key is a 400.
10064Up to 50 items of up to 500 characters. Replaces the whole list; null empties it.
50500Up to 50 items of up to 500 characters. Replaces the whole list; null empties it.
50500Up to 4,000 characters. null clears it.
4000Cannot be cleared.
never, sometimes, often Up to 80 characters. null clears it.
80Up to 160 characters. null clears it.
160Up to 120 characters. null clears it.
120Up to 120 characters. null clears it.
120Up to 1,000 characters. null clears it.
10000 to 1000000 cents. null clears it.
0 <= x <= 10000000 to 1000000 cents. null clears it.
0 <= x <= 10000000 to 1000000 cents. null clears it.
0 <= x <= 1000000A BCP-47 tag (en, pt-BR), up to 20 characters. null puts back en.
20Cannot be cleared.
proper, relaxed, minimal A valid IANA time zone (America/Chicago). null puts back UTC.
64Cannot be cleared.
fast, natural, slow Response
The whole persona after the change.
The AI persona: the brief the AI chatter follows to chat as this creator. Every field is written by your team.
How her page works: free or paid, what is on the wall, what is sold in messages.
Other languages she can chat in (BCP-47 tags).
Her age, 18 to 99. Required for the AI.
Whether fans are told they may be talking to an AI.
The disclosure fans see when aiDisclosureEnabled is on.
Whether the AI may make an occasional natural typo.
Her overall character, for example girl_next_door.
Topics she avoids or steers away from, in plain sentences.
How she capitalizes: normal, all lowercase, or a casual_mix.
Where she says she lives. Required for the AI.
How complete the persona is, 0 to 100.
The kinds of content she makes.
Anything else about her content: lengths, what is shown, what is never shown.
Whether the AI may quote custom content at the prices below.
Days she needs to deliver a custom photo. null uses the default of 3.
Days she needs to deliver a custom video. null uses the default of 7.
How many emojis she uses: none, rare, some, heavy.
Sample messages in her voice. The strongest single influence on how the AI sounds.
Every hand-off kind you can choose from (the example shows three of them).
Show child attributes
Show child attributes
The hand-off kinds actually in force: your handoffKinds (or the recommended set) plus the locked kinds.
The hand-off kinds (keys from handoffCatalogue) that make the AI hand a chat to your team. null means the recommended set; [] means only the kinds that can never be switched off.
Things she never does or offers, whatever a fan says. The AI treats them as a hard stop.
Things she likes talking about.
Her backstory in her own world: family, home, routine, personality. Required for the AI.
How often one reply is split into several short messages: never, sometimes, often.
A pet name or short name.
What she does besides OnlyFans. Required for the AI.
Where she is from.
The first name she uses with fans. Required for the AI.
What she looks like, so the AI never contradicts her content.
Minimum price for a custom photo, in cents.
Minimum total price for any custom video, in cents.
Minimum price per minute of custom video, in cents.
The language she chats in, as a BCP-47 tag (en, es, pt-BR).
How carefully she punctuates: proper, relaxed or minimal.
true when every required field is filled: personaName, age, city, occupation and lore.
Required fields that are still empty.
Her IANA time zone (America/Chicago). The AI's sense of her local time uses it.
How quickly replies go out, like a person typing: fast, natural, slow.
When the persona was last changed; null if it was never written.