API reference
Health, identity and locations
Liveness, the caller's identity and the active locations.
Start here to find out who a token belongs to (GET /v1/me), which organizations and roles it has, and which locations resources can be created in.
GET /health
Liveness check. No authentication.
Auth: none
Response 200
{
ok: true
}GET /health/deep
Liveness check including a read of the serving region's database replica; returns the region. No authentication.
Auth: none
Response 200
{
ok: true
region: string
dbMs: number
}GET /v1/hooks/people/:userId/avatar
A person's avatar for <img src> anywhere: a redirect to the image (good for an hour), cached for ten minutes; 404 when they have none. The avatarUrl in profiles carries a version (?v=), so a new image is a new URL. No authentication.
Auth: none
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
GET /v1/hooks/people/:userId/banner
A person's banner image for <img src> anywhere, as the avatar's hook: a redirect to the image, cached for ten minutes; 404 when they have none. bannerUrl carries a version. No authentication.
Auth: none
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
GET /v1/me
The caller: the signed-in user (with organizations and roles), or the key (API or platform agent key) with its organization and scopes.
Auth: user access token or platform agent key
Response 200
{
type: "agent"
id: string
orgId: string
scopes: ("platform:admin" | "platform:infra" | "platform:analytics" | "platform:coverage" | "org:read" | "org:write" | "audit:read" | "tenant:read" | "tenant:write" | "members:read" | "members:write" | "projects:read" | "projects:write" | "deployments:read" | "deployments:write" | "env:read" | "env:write" | "keys:read" | "keys:use" | "keys:write" | "domains:read" | "domains:write" | "resources:read" | "resources:write" | "git:read" | "git:write" | "git:admin" | "logs:read" | "analytics:read" | "knowledge:read" | "knowledge:write" | "connectors:read" | "connectors:write" | "chat:use" | "mcp:connect" | "agents:read" | "agents:write" | "agents:run" | "mail:read" | "mail:send" | "mail:admin" | "calendar:read" | "calendar:write" | "contacts:read" | "contacts:write" | "issues:read" | "issues:write" | "issues:admin" | "maps:read" | "maps:write" | "drive:read" | "drive:write" | "crm:read" | "crm:write" | "crm:admin" | "marketing:read" | "marketing:write" | "marketing:send" | "marketing:admin" | "health:read" | "health:write" | "weather:read" | "weather:write" | "food:read" | "food:write")[]
name?: string
userId?: string
creatorRole?: "owner" | "admin" | "developer" | "viewer"
personal?: {
kind: "photos"
scopes: string[]
}
bot?: {
appId: string
}
} | {
userId: string
email: string
name?: string
avatarUrl?: string
locale?: string
orgs: {
id: string
slug: string
name?: string
kind: "platform" | "customer"
role: "owner" | "admin" | "developer" | "viewer"
teams?: string[]
scopes?: string[]
pending?: {
requirement: "mfa" | "passkey" | "method"
until: number
}
}[]
held: {
id: string
slug: string
name?: string
reason: "blocked" | "guest_expired" | "mfa_required" | "passkey_required" | "method_not_allowed" | "reauthenticate" | "session_too_old"
}[]
isPlatformAdmin: boolean
type: string
}GET /v1/locales
The languages people can choose (locales: locale, label, spelling, weekStart), in order: English (Australia), the default, and English (US). Choose one with PATCH /v1/me/profile (locale).
Auth: user access token or platform agent key
Response 200
{
locales: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
}[]
}GET /v1/me/org-access
Your organizations whose Cactive One access has ended (never subscribed, cancelled or unpaid), with the reason for each ({ ended: { [orgId]: reason } }). Their apps answer 402 with code: "access_ended" until an administrator fixes billing.
Auth: user access token or platform agent key
Response 200
{
ended: {
[key: string]: "canceled" | "unpaid" | "setup"
}
}GET /v1/locations
Active locations, for location pickers. Any authenticated caller.
Auth: user access token or platform agent key
Response 200
{
locations: {
locationId: string
label: string
isDefault: boolean
regions: string[]
region?: string
}[]
}GET /v1/me/preferences
Your preferences, the same in every app and on every device: apps, the app switcher's order (order, tile keys such as cloud or mail:contacts) and the apps you hid (hidden), or null for the default switcher. People only.
Auth: user access token or platform agent key
Response 200
{
apps: null | {
order: string[]
hidden: string[]
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
PUT /v1/me/preferences
Saves your app switcher (apps: { order, hidden }, up to 100 keys each; apps added later appear at the end). apps: null restores the default. People only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
apps | object | Yes | can be null |
apps.order | string[] | Yes | up to 100 items; each matches ^[a-z0-9][a-z0-9:_-]{0,39}$ |
apps.hidden | string[] | Yes | up to 100 items; each matches ^[a-z0-9][a-z0-9:_-]{0,39}$ |
Response 200
{
apps: null | {
order: string[]
hidden: string[]
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
GET /v1/me/preferences/widgets
The phone's widgets: what the Dynamic Island and the Lock Screen show, ranked per surface; null until the person arranges them.
Auth: user access token or platform agent key
Response 200
{
widgets: null | {
v: 1
items: {
id: string
kind: "counter"
title?: string
config: {
label: string
step: number
value: number
goal: number
}
} | {
id: string
kind: "device"
title?: string
config: {
show: "thermal" | "battery" | "memory" | "cpu"
}
} | {
id: string
kind: "endpoint"
title?: string
config: {
label: string
url: string
path: string
unit: string
decimals: null | number
refreshMinutes: number
}
} | {
id: string
kind: "photo"
title?: string
config: {
photos: string[]
rotate: "never" | "hourly" | "daily"
}
} | {
id: string
kind: "weather"
title?: string
config: {
place: "saved" | "current"
placeId: string
name: string
lat: null | number
lon: null | number
units: "auto" | "metric" | "imperial"
}
} | {
id: string
kind: "activity"
title?: string
config: {
metric: "steps" | "distance"
unit: "km" | "mi"
goal: number
}
} | {
id: string
kind: "quote"
title?: string
config: {}
} | {
id: string
kind: "reminders"
title?: string
config: {
list: string
overdue: boolean
}
} | {
id: string
kind: "weekday"
title?: string
config: {
style: "short" | "long"
showDate: boolean
}
} | {
id: string
kind: "flight"
title?: string
config: {
radiusKm: number
}
} | {
id: string
kind: "dinner"
title?: string
config: {
picture: boolean
}
} | {
id: string
kind: "dayProgress"
title?: string
config: {
period: "month" | "day" | "year" | "week"
start: string
end: string
weekStartsOn: "monday" | "sunday"
show: "remaining" | "percent"
photo: string
}
} | {
id: string
kind: "countdown"
title?: string
config: {
label: string
target: "date" | "endOfDay" | "endOfWeek" | "endOfMonth" | "endOfYear"
at: null | number
repeat: "none" | "yearly"
weekStartsOn: "monday" | "sunday"
show: "auto" | "days"
photo: string
}
} | {
id: string
kind: "pomodoro"
title?: string
config: {
focusMinutes: number
shortBreakMinutes: number
longBreakMinutes: number
sprintsPerLongBreak: number
autoStart: boolean
dailyGoal: number
}
} | {
id: string
kind: "stocks"
title?: string
config: {
symbols: string[]
mode: "fixed" | "carousel"
change: "amount" | "percent"
}
} | {
id: string
kind: "agenda"
title?: string
config: {
events: boolean
tasks: boolean
phoneCalendars: boolean
days: number
}
} | {
id: string
kind: "network"
title?: string
config: {
speedTest: boolean
sizeKb: number
}
} | {
id: string
kind: "stopwatch"
title?: string
config: {
label: string
}
} | {
id: string
kind: "shopping"
title?: string
config: {
count: number
}
} | {
id: string
kind: "progressBars"
title?: string
config: {
rows: {
label: string
source: string
color: "success" | "auto" | "warning" | "brand" | "destructive" | "chart1" | "chart2" | "chart3" | "chart4" | "chart5"
max: number
}[]
weekStartsOn: "monday" | "sunday"
}
}[]
island: {
enabled: boolean
order: string[]
}
lockScreen: {
enabled: boolean
order: string[]
maxItems: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
PUT /v1/me/preferences/widgets
Saves the phone's widgets (at most 16 KB as JSON); every phone signed in to the account follows.
Auth: user access token or platform agent key
Request body (up to 20 KB)
| Field | Type | Required | Notes |
|---|---|---|---|
widgets | object | Yes | can be null |
widgets.v | 1 | Yes | |
widgets.items | object[] | Yes | up to 32 items |
widgets.items[].id | string | Yes | matches ^[a-z0-9][a-z0-9_-]{0,31}$ |
widgets.items[].kind | string | Yes | up to 32 characters |
widgets.items[].title | string | No | up to 200 characters |
widgets.items[].config | object | Yes | values: any JSON |
widgets.island | object | Yes | |
widgets.island.enabled | boolean | Yes | |
widgets.island.order | string[] | Yes | up to 32 items; each matches ^[a-z0-9][a-z0-9_-]{0,31}$ |
widgets.lockScreen | object | Yes | |
widgets.lockScreen.enabled | boolean | Yes | |
widgets.lockScreen.order | string[] | Yes | up to 32 items; each matches ^[a-z0-9][a-z0-9_-]{0,31}$ |
widgets.lockScreen.maxItems | integer | Yes | 1–4 |
Response 200
{
widgets: null | {
v: 1
items: {
id: string
kind: "counter"
title?: string
config: {
label: string
step: number
value: number
goal: number
}
} | {
id: string
kind: "device"
title?: string
config: {
show: "thermal" | "battery" | "memory" | "cpu"
}
} | {
id: string
kind: "endpoint"
title?: string
config: {
label: string
url: string
path: string
unit: string
decimals: null | number
refreshMinutes: number
}
} | {
id: string
kind: "photo"
title?: string
config: {
photos: string[]
rotate: "never" | "hourly" | "daily"
}
} | {
id: string
kind: "weather"
title?: string
config: {
place: "saved" | "current"
placeId: string
name: string
lat: null | number
lon: null | number
units: "auto" | "metric" | "imperial"
}
} | {
id: string
kind: "activity"
title?: string
config: {
metric: "steps" | "distance"
unit: "km" | "mi"
goal: number
}
} | {
id: string
kind: "quote"
title?: string
config: {}
} | {
id: string
kind: "reminders"
title?: string
config: {
list: string
overdue: boolean
}
} | {
id: string
kind: "weekday"
title?: string
config: {
style: "short" | "long"
showDate: boolean
}
} | {
id: string
kind: "flight"
title?: string
config: {
radiusKm: number
}
} | {
id: string
kind: "dinner"
title?: string
config: {
picture: boolean
}
} | {
id: string
kind: "dayProgress"
title?: string
config: {
period: "month" | "day" | "year" | "week"
start: string
end: string
weekStartsOn: "monday" | "sunday"
show: "remaining" | "percent"
photo: string
}
} | {
id: string
kind: "countdown"
title?: string
config: {
label: string
target: "date" | "endOfDay" | "endOfWeek" | "endOfMonth" | "endOfYear"
at: null | number
repeat: "none" | "yearly"
weekStartsOn: "monday" | "sunday"
show: "auto" | "days"
photo: string
}
} | {
id: string
kind: "pomodoro"
title?: string
config: {
focusMinutes: number
shortBreakMinutes: number
longBreakMinutes: number
sprintsPerLongBreak: number
autoStart: boolean
dailyGoal: number
}
} | {
id: string
kind: "stocks"
title?: string
config: {
symbols: string[]
mode: "fixed" | "carousel"
change: "amount" | "percent"
}
} | {
id: string
kind: "agenda"
title?: string
config: {
events: boolean
tasks: boolean
phoneCalendars: boolean
days: number
}
} | {
id: string
kind: "network"
title?: string
config: {
speedTest: boolean
sizeKb: number
}
} | {
id: string
kind: "stopwatch"
title?: string
config: {
label: string
}
} | {
id: string
kind: "shopping"
title?: string
config: {
count: number
}
} | {
id: string
kind: "progressBars"
title?: string
config: {
rows: {
label: string
source: string
color: "success" | "auto" | "warning" | "brand" | "destructive" | "chart1" | "chart2" | "chart3" | "chart4" | "chart5"
max: number
}[]
weekStartsOn: "monday" | "sunday"
}
}[]
island: {
enabled: boolean
order: string[]
}
lockScreen: {
enabled: boolean
order: string[]
maxItems: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
GET /v1/me/preferences/calendar
Calendar's settings (week start, working hours, other time zones, new-event defaults, overlays, hidden calendars); null until changed.
Auth: user access token or platform agent key
Response 200
{
calendar: null | {
weekStart: null | number
workStart: number
workEnd: number
workDays: number[]
zones: string[]
defaultReminder: null | number
defaultLength: number
showWeekends: boolean
showDeclined: boolean
showTasks: boolean
showBirthdays: boolean
hidden: string[]
defaultView: "month" | "day" | "year" | "week" | "agenda"
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
PUT /v1/me/preferences/calendar
Saves Calendar's settings; the answer is what was saved (cleaned). Every device and the phone follow.
Auth: user access token or platform agent key
Request body (up to 16 KB)
| Field | Type | Required | Notes |
|---|---|---|---|
calendar | object | Yes | values: any JSON; can be null |
Response 200
{
calendar: null | {
weekStart: null | number
workStart: number
workEnd: number
workDays: number[]
zones: string[]
defaultReminder: null | number
defaultLength: number
showWeekends: boolean
showDeclined: boolean
showTasks: boolean
showBirthdays: boolean
hidden: string[]
defaultView: "month" | "day" | "year" | "week" | "agenda"
}
}Errors
| Status | Message |
|---|---|
403 | Preferences belong to people. |
403 | Change preferences in the app. |
413 | Too many settings to save. |
GET /v1/me/profile
You: your profile (name, email, avatarUrl, bannerUrl, banner and accent colours, about (your About Me), effect, username, kind (personal, or org for an account made through an organization's invite) and usernameChangeAt (when it can change next, or null), locale (the language you chose, or null)), the language to use (locale: your choice, else en-US for a US Accept-Language, else en-AU), the languages you can choose (locales) and each organization with your scopes there, so native apps offer only the organizations an app can open. People only.
Auth: user access token or platform agent key
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
locale: string
locales: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
}[]
orgs: {
id: string
slug: string
name?: string
kind: "platform" | "customer"
role: "owner" | "admin" | "developer" | "viewer"
scopes: ("platform:admin" | "platform:infra" | "platform:analytics" | "platform:coverage" | "org:read" | "org:write" | "audit:read" | "tenant:read" | "tenant:write" | "members:read" | "members:write" | "projects:read" | "projects:write" | "deployments:read" | "deployments:write" | "env:read" | "env:write" | "keys:read" | "keys:use" | "keys:write" | "domains:read" | "domains:write" | "resources:read" | "resources:write" | "git:read" | "git:write" | "git:admin" | "logs:read" | "analytics:read" | "knowledge:read" | "knowledge:write" | "connectors:read" | "connectors:write" | "chat:use" | "mcp:connect" | "agents:read" | "agents:write" | "agents:run" | "mail:read" | "mail:send" | "mail:admin" | "calendar:read" | "calendar:write" | "contacts:read" | "contacts:write" | "issues:read" | "issues:write" | "issues:admin" | "maps:read" | "maps:write" | "drive:read" | "drive:write" | "crm:read" | "crm:write" | "crm:admin" | "marketing:read" | "marketing:write" | "marketing:send" | "marketing:admin" | "health:read" | "health:write" | "weather:read" | "weather:write" | "food:read" | "food:write")[]
}[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
PATCH /v1/me/profile
Changes your profile, the one people see across the platform (Mirage's cards, members and messages; every app's account menu): name, about (About Me, Markdown as in Mirage), banner and accent (hex colours such as #5865f2) effect (a profile effect's id from GET /v1/me/profile/effects) and locale (your language for dates, numbers and spelling in every app, email and text: one of GET /v1/locales, such as en-AU or en-US). null clears a field (not the name). Returns user and the fields that changed; name changes are recorded in your organizations' audit logs. People only, not assistants.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | up to 100 characters |
about | string | No | up to 190 characters; can be null |
banner | string | No | matches ^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$; can be null |
accent | string | No | matches ^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$; can be null |
effect | string | No | up to 40 characters; can be null |
locale | string | No | up to 20 characters; can be null |
showBadges | boolean | No |
Also checked: Unknown fields are rejected.
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
POST /v1/me/profile/avatar/uploads
Where to upload a new avatar: give its type (PNG, JPEG, WebP or GIF) and size in bytes (up to 2 MB). PUT the image to url with headers within 15 minutes (exactly that type and size), then make it your avatar with PUT /v1/me/profile/avatar. People only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
type | "image/png" | "image/jpeg" | "image/webp" | "image/gif" | Yes | |
size | integer | Yes | 1–2097152 |
Also checked: Unknown fields are rejected.
Response 201
{
uploadId: string
url: string
headers: {
"content-type": "image/gif" | "image/jpeg" | "image/png" | "image/webp"
}
expiresAt: number
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
PUT /v1/me/profile/avatar
Makes an uploaded image (uploadId) your avatar once it's checked: there, up to 2 MB and really a PNG, JPEG, WebP or GIF, whatever it was uploaded as. Anything else is deleted (400 or 413). The previous image is deleted. Returns user with the new avatarUrl. People only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
uploadId | string | Yes | matches ^avt_[0-9a-z]{26}$ |
Also checked: Unknown fields are rejected.
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
DELETE /v1/me/profile/avatar
Removes your avatar (people see your initials) and deletes the image. People only.
Auth: user access token or platform agent key
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
POST /v1/me/profile/banner/uploads
Where to upload a new banner image (cropped to 5:2, 1500 × 600, first): give its type (PNG, JPEG, WebP or GIF) and size in bytes (up to 4 MB). PUT the image to url with headers within 15 minutes, then make it your banner with PUT /v1/me/profile/banner. People only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
type | "image/png" | "image/jpeg" | "image/webp" | "image/gif" | Yes | |
size | integer | Yes | 1–4194304 |
Also checked: Unknown fields are rejected.
Response 201
{
uploadId: string
url: string
headers: {
"content-type": "image/gif" | "image/jpeg" | "image/png" | "image/webp"
}
expiresAt: number
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
PUT /v1/me/profile/banner
Makes an uploaded image (uploadId) your banner once it's checked: there, up to 4 MB and really a PNG, JPEG, WebP or GIF. Anything else is deleted (400 or 413). The previous image is deleted. Returns user with the new bannerUrl. People only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
uploadId | string | Yes | matches ^avt_[0-9a-z]{26}$ |
Also checked: Unknown fields are rejected.
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
DELETE /v1/me/profile/banner
Removes your banner image (your banner colour shows) and deletes the image. People only.
Auth: user access token or platform agent key
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
GET /v1/me/profile/effects
The profile effects you can choose (effects: effectId, name, renderer, colors), in order. People only.
Auth: user access token or platform agent key
Response 200
{
effects: {
effectId: string
name: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
GET /v1/me/profile/username/check
Whether a username (name) could be yours: available, or the reason it can't (taken, reserved, or not 2–32 lowercase letters, numbers, underscores and single full stops). People only.
Auth: user access token or platform agent key
| Query parameter | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | up to 64 characters |
Response 200
{
username: string
available: boolean
reason: null | string
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |
PUT /v1/me/profile/username
Claims or changes your username (username, unique regardless of case). After a change the next waits 30 days (429); your previous name is held for you for 14 days. Accounts made through an organization's invite have none (403); a taken name is 409. Recorded in your organizations' audit logs. People only, not assistants.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
username | string | Yes | up to 64 characters |
Also checked: Unknown fields are rejected.
Response 200
{
user: {
userId: string
name: string
avatarUrl: null | string
bannerUrl: null | string
banner: null | string
accent: null | string
about: null | string
effect: null | {
id: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
}
username: null | string
kind: "org" | "personal"
badges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
email: string
createdAt: number
usernameChangeAt: null | number
showBadges: boolean
ownBadges: {
kind: "early-supporter"
since: null | number
} | {
kind: "subscriber"
since: number
tier: {
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
id: string
}
}[]
locale: null | string
}
changed: ("name" | "avatar" | "bannerImage" | "banner" | "accent" | "about" | "effect" | "username" | "locale" | "showBadges")[]
}Errors
| Status | Message |
|---|---|
403 | A personal session is required. |
403 | Change your profile in the app. |